Tensorflow.NET是基于Tensorflow封装的一个开源C#的库 接下来我们将试着使用他搭建自己的学习分类任务
1.下载安装下面的包直接在Nuget里面搜索安装即可
TensorFlow.Keras
SciSharp.TensorFlow.Redist
OpenCvSharp4
OpenCvSharp4.runtime.win
2、开始搭建可以直接赋值官网的代码根据自己的需要在调整网络 这里我们直接赋值官方实例
var layers = keras.layers;
var inputs = keras.Input(shape: (400, 300, 3), name: "img"); //这里注意一下H W 后面3 是图片通道数
// convolutional layer
var x = layers.Conv2D(32, 3, activation: "relu").Apply(inputs);
x = layers.Conv2D(64, 3, activation: "relu").Apply(x);
var block_1_output = layers.MaxPooling2D(3).Apply(x);
x = layers.Conv2D(64, 3, activation: "relu", padding: "same").Apply(block_1_output);
x = layers.Conv2D(64, 3, activation: "relu", padding: "same").Apply(x);
var block_2_output = layers.Add().Apply(new Tensors(x, block_1_output));
x = layers.Conv2D(64, 3, activation: "relu", padding: "same").Apply(block_2_output);
x = layers.Conv2D(64, 3, activation: "relu", padding: "same").Apply(x);
var block_3_output = layers.Add().Apply(new Tensors(x, block_2_output));
x = layers.Conv2D(64, 3, activation: "relu").Apply(block_3_output);
x = layers.GlobalAveragePooling2D().Apply(x);
x = layers.Dense(256, activation: "relu").Apply(x);
x = layers.Dropout(0.5f).Apply(x);
// output layer
var outputs = layers.Dense(2).Apply(x);//写自己的分类数量 有两个类别就写2
// build keras model
var model = keras.Model(inputs, outputs, name: "toy_resnet");
model.summary();
// compile keras model in tensorflow static graph
model.compile(optimizer: keras.optimizers.RMSprop(1e-3f),
loss: keras.losses.SparseCategoricalCrossentropy(from_logits: true),
metrics: new[] { "acc" });
var ((x_train, y_train), (x_test, y_test)) = cifar10Data.load_data();// keras.datasets.cifar10.load_data();
x_train = x_train / 255.0f;
// Train model
model.fit(x_train, y_train, batch_size: 10, epochs: 30, validation_split: 0.2f);
// Save model
model.save("自己模型保存路径");
上面的 cifar10Data.load_data()是自己的路径 这里我封装了一个直接可以使用 使用时候注意 目录结构
cifar10Data的代码如下
1 using OpenCvSharp;
2 using System;
3 using System.Collections.Generic;
4 using System.IO;
5 using System.Linq;
6 using System.Runtime.InteropServices;
7 using System.Threading.Tasks;
8 using Tensorflow;
9 using Tensorflow.Keras.Datasets;
10 using Tensorflow.NumPy;
11 using Tensorflow.Operations.Initializers;
12 using static Tensorflow.Binding;
13 using static Tensorflow.KerasApi;
14
15 public class Cifar10Data
16 {
17 // 本地数据集根目录
18 private readonly string _datasetRoot = "E:\\Modelinmg\\tf001";
19 private readonly string _trainFolder;
20 private readonly string _testFolder;
21
22 // 图片尺寸:宽300,高400
23 private const int ImgWidth = 300;
24 private const int ImgHeight = 400;
25 private const int Channel = 3;
26
27 public Cifar10Data()
28 {
29 _trainFolder = Path.Combine(_datasetRoot, "train");//训练数据
30 _testFolder = Path.Combine(_datasetRoot, "test");//测试数据
31 }
32
33 public DatasetPass load_data()
34 {
35 var (trainX, trainY) = LoadImageFolder(_trainFolder);
36 var (testX, testY) = LoadImageFolder(_testFolder);
37
38 return new DatasetPass
39 {
40 Train = (trainX, trainY),
41 Test = (testX, testY)
42 };
43 }
44
45 private (NDArray imageNd, NDArray labelNd) LoadImageFolder(string folderPath)
46 {
47 if (!Directory.Exists(folderPath))
48 throw new DirectoryNotFoundException($"数据集文件夹不存在:{folderPath}");
49
50 var classDirs = Directory.GetDirectories(folderPath).OrderBy(p => p).ToList();
51 Dictionary<string, int> classLabelMap = new Dictionary<string, int>();
52 for (int i = 0; i < classDirs.Count; i++)
53 {
54 string className = Path.GetFileName(classDirs[i]);
55 classLabelMap[className] = i;
56 }
57 //var shuffledList= classLabelMap.ToList();
58 // Random rng = new Random();
59 // shuffledList = shuffledList.OrderBy(x => rng.Next()).ToList();
60 // // 如果需要重新放回字典(但字典本身仍然无序)
61 // classLabelMap = new Dictionary<string, int>(shuffledList);
62 List<string> imageMatList = new List<string>();
63 List<int> labelList = new List<int>();
64
65 foreach (var classDir in classDirs)
66 {
67 string className = Path.GetFileName(classDir);
68 int label = classLabelMap[className];
69 var imgPaths = Directory.GetFiles(classDir, "*.*")
70 .Where(p => p.EndsWith(".jpg") || p.EndsWith(".jpeg") || p.EndsWith(".png"));
71
72 foreach (var imgPath in imgPaths)
73 {
74 imageMatList.Add(imgPath);
75 labelList.Add(label);
76 }
77 }
78
79 var pairList = imageMatList.Zip(labelList, (path, label) => new { Img = path, Label = label }).ToList();
80 Random random = new Random(42);
81 pairList = pairList.OrderBy(_ => random.Next()).ToList();
82 imageMatList.Clear();
83 labelList.Clear();
84 foreach (var item in pairList)
85 {
86 imageMatList.Add(item.Img);
87 labelList.Add(item.Label);
88 }
89
90 int sampleCount = imageMatList.Count;//数据总量
91 if (sampleCount == 0)
92 throw new Exception($"文件夹 {folderPath} 未找到任何有效图片");
93
94 // ========== 核心修复:直接创建NDArray,删除全局超大byte[] ==========
95 long[] imgShape = { sampleCount, ImgWidth, ImgHeight, Channel };
96 //NDArray imageND = np.zeros(imgShape, np.float32);
97 NDArray imageNd = np.zeros(imgShape, np.uint8);
98
99 Mat bgrImg = new Mat();
100 Mat rgbImg = new Mat();
101 //Mat[] channels = null;
102 int idx = 0;
103 //byte[] bytes = new byte[ImgHeight * ImgWidth * Channel* sampleCount];
104 int singleByte = ImgHeight * ImgWidth * 3;
105 foreach (var item in imageMatList)
106 {
107 bgrImg = Cv2.ImRead(item, ImreadModes.Color);
108 if (bgrImg.Empty())
109 {
110 Console.WriteLine($"跳过损坏/无法读取图片:{item}");
111 bgrImg.Dispose();
112 continue;
113 }
114 if (bgrImg.Channels() != Channel)
115 {
116 Console.WriteLine($"跳过非3通道图片:{item}");
117 bgrImg.Dispose();
118 continue;
119 }
120 if (bgrImg.Rows != ImgHeight || bgrImg.Cols != ImgWidth)
121 {
122 bgrImg.Dispose();
123 throw new Exception($"图片尺寸不匹配 {item},预期高{ImgHeight}×宽{ImgWidth}");
124 }
125 Cv2.CvtColor(bgrImg, rgbImg, ColorConversionCodes.BGR2RGB);
126 // 单张图片总字节 H*W*3
127 byte[] pixelData = new byte[singleByte];
128 // 直接拷贝整张RGB图像内存,天然NHWC排布
129 Marshal.Copy(rgbImg.Data, pixelData, 0, singleByte);
130 // 一维数组直接重塑 (H,W,3),无任何通道转换
131 imageNd[idx] = np.array(pixelData).reshape((ImgHeight, ImgWidth, 3));
132 idx++;
133 //rgbImg.SetTo(0);
134 }
135 bgrImg.Dispose();
136 rgbImg.Dispose();
137 // 归一化转float32
138 // imageNd = imageNd.astype(np.float32);
139 //imageNd = imageNd / 255.0f;
140
141 NDArray labelNd = np.array(labelList.ToArray()).astype(np.int32);
142 return (imageNd, labelNd);
143 }
144
145 public NDArray oneImg(string imgpath)
146 {
147
148 Mat bgrImg = new Mat();
149 Mat rgbImg = new Mat();
150 bgrImg = Cv2.ImRead(imgpath, ImreadModes.Color);
151 if (bgrImg.Empty())
152 {
153 bgrImg.Dispose();
154 throw new Exception($"跳过损坏/无法读取图片:{imgpath}");
155 }
156 if (bgrImg.Channels() != Channel)
157 {
158 bgrImg.Dispose();
159 throw new Exception("$\"跳过非3通道图片:{imgpath}\"");
160 }
161 Cv2.CvtColor(bgrImg, rgbImg, ColorConversionCodes.BGR2RGB);
162 int singleByte = rgbImg.Width * rgbImg.Height * 3;
163 // 单张图片总字节 H*W*3
164 byte[] pixelData = new byte[singleByte];
165 // 直接拷贝整张RGB图像内存,天然NHWC排布
166 Marshal.Copy(rgbImg.Data, pixelData, 0, singleByte);
167 // 一维数组直接重塑 (H,W,3),无任何通道转换
168 int[] shape = new int[] { 1, rgbImg.Height, rgbImg.Width, 3 };
169 return np.array(pixelData).reshape(shape);
170
171 }
172 //模型预测
173 public int Modelpredict(string Modelpath, string imgpath)
174 {
175 NDArray nD= oneImg(imgpath);
176 //NDArray input_batch = np.expand_dims(nD, axis: 0);
177 var modeltest = keras.models.load_model(Modelpath);
178 nD = nD / 255.0f;
179 var logits = modeltest.predict(nD);
180 NDArray prob = tf.nn.softmax(logits).numpy();
181 //5.找出概率最大下标,就是预测类别
182 int predictClass = np.argmax(prob[0]);
183
184 return predictClass;
185 }
186 }