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 }