一、训练模型
using OpenCvSharp; using OpenCvSharp.Extensions; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Threading; using System.IO; namespace app { public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } /// <summary> /// knn手写数字识别 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnKNN_Click(object sender, EventArgs e) { // 训练数据数量 int train_sample_count = 60000; // 测试数据数量 int test_sample_count = 10000; // 声明训练数据集合 mat,60000行,784列 Mat trainData = new Mat(train_sample_count, 28 * 28, MatType.CV_32FC1); // 声明测试数据集合 mat,10000行,784列 Mat testData = new Mat(test_sample_count, 28 * 28, MatType.CV_32FC1); // 声明训练数据标签 mat,60000行,1列 Mat trainLabel = new Mat(train_sample_count, 1, MatType.CV_32FC1); // 声明测试数据标签 mat,10000行,1列 Mat testLabel = new Mat(test_sample_count, 1, MatType.CV_32FC1); string trainPath = @"img\mnist\train_images"; string testPath = @"img\mnist\test_images"; // 组织训练数据,循环训练文件夹内所有图片。 int trainNum = 0; for (int i = 0; i < 10; i++) { string path = trainPath + "\\" + i; DirectoryInfo TheFolder = new DirectoryInfo(path); foreach (FileInfo NextFile in TheFolder.GetFiles()) { // 读入单通道灰度图 Mat temp = new Mat(NextFile.FullName, ImreadModes.Grayscale); // 转换CV_32FC1,因为下面训练函数需要这个格式 temp.ConvertTo(temp, MatType.CV_32FC1); // 写入到训练数据集合的mat内,注意reshape的用法。 /* reshape有两个参数: 其中,参数:cn为新的通道数,如果cn = 0,表示通道数不会改变。 参数rows为新的行数,如果rows = 0,表示行数不会改变。 注意:新的行* 列必须与原来的行*列相等。就是说,如果原来是5行3列,新的行和列可以是1行15列,3行5列,5行3列,15行1列。 设置行数后,列数会自动调整。比如此处 调整为 1行784列。 */ temp.Reshape(0, 1).CopyTo(trainData.Row(trainNum)); // 写入到训练标签集合的mat内 trainLabel.Set<float>(trainNum, i); trainNum++; } } // 组织测试数据 int testNum = 0; for (int i = 0; i < 10; i++) { string path = testPath + "\\" + i; DirectoryInfo TheFolder = new DirectoryInfo(path); foreach (FileInfo NextFile in TheFolder.GetFiles()) { Mat temp = new Mat(NextFile.FullName, ImreadModes.Grayscale); temp.ConvertTo(temp, MatType.CV_32FC1); temp.Reshape(0, 1).CopyTo(testData.Row(testNum)); testLabel.Set<float>(testNum, i); testNum++; } } // 创建knn模型 OpenCvSharp.ML.KNearest knn = OpenCvSharp.ML.KNearest.Create(); // k 可以根据需要自行调整 int k = 3; // 设置K值 knn.DefaultK = k; // 设置KNN是进行分类还是回归 knn.IsClassifier = true; // 设置算法类型 BruteForce 或 KdTree knn.AlgorithmType = OpenCvSharp.ML.KNearest.Types.BruteForce; // 训练 knn.Train(trainData, OpenCvSharp.ML.SampleTypes.RowSample, trainLabel); // 测试 Mat result = new Mat(test_sample_count, 1, MatType.CV_32FC1); knn.FindNearest(testData, k, result); int t = 0; int f = 0; for (int i = 0; i < test_sample_count; i++) { int predict = (int)result.At<float>(i); int actual = (int)testLabel.At<float>(i); if (predict == actual) { System.Console.WriteLine("正确:" + predict + "-" + actual); t++; } else { System.Console.WriteLine("错误------:" + predict + "-" + actual); f++; } } double accuracy = (t * 1.0) / (t + f); System.Console.WriteLine("准确率:" + accuracy); } } }
二、保存模型
训练完成之后,可以用knn.Save()保存模型文件。
// 训练 knn.Train(trainData, OpenCvSharp.ML.SampleTypes.RowSample, trainLabel); // 保存模型 knn.Save(@"D:\opencv\mnist\knn_digits_model2023.yml");
三、加载模型 【2023-03-18 更新】
近期咨询模型加载的同学比较多,因此增加了示范代码。测试数据可以另外指定数据。
关于图片目录 0、1、2、3、4、5、6、7、8、9,是为了分析预测值与真实值是否一致,即预测的准确性。
模型训练完成之后,可以指定任意文件夹的图片进行预测(也可以自定义手写数字,根据需要修改示范代码)。
/// <summary> /// 测试 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnKNNTest_Click(object sender, EventArgs e) { // 测试数据数量 int test_sample_count = 10000; // 声明测试数据集合 mat,10000行,784列 Mat testData = new Mat(test_sample_count, 28 * 28, MatType.CV_32FC1); // 声明测试数据标签 mat,10000行,1列 Mat testLabel = new Mat(test_sample_count, 1, MatType.CV_32FC1); string testPath = @"D:\opencv\mnist\test_images"; // 创建knn模型 OpenCvSharp.ML.KNearest knn = OpenCvSharp.ML.KNearest.Load(@"D:\opencv\mnist\knn_digits_model2023.yml"); // 组织测试数据 int testNum = 0; for (int i = 0; i < 10; i++) { string path = testPath + "\\" + i; DirectoryInfo TheFolder = new DirectoryInfo(path); foreach (FileInfo NextFile in TheFolder.GetFiles()) { Mat temp = new Mat(NextFile.FullName, ImreadModes.Grayscale); temp.ConvertTo(temp, MatType.CV_32FC1); temp.Reshape(0, 1).CopyTo(testData.Row(testNum)); testLabel.Set<float>(testNum, i); testNum++; } } int k = 3; // 测试 Mat result = new Mat(test_sample_count, 1, MatType.CV_32FC1); // 在训练样本中寻找最接近待分类样本的前K个样本,并返回K个样本中数量最多类型的标签。 knn.FindNearest(testData, k, result); int t = 0; int f = 0; for (int i = 0; i < test_sample_count; i++) { int predict = (int)result.At<float>(i); int actual = (int)testLabel.At<float>(i); if (predict == actual) { System.Console.WriteLine("正确:" + predict + "-" + actual); t++; } else { System.Console.WriteLine("错误------:" + predict + "-" + actual); f++; } } double accuracy = (t * 1.0) / (t + f); System.Console.WriteLine("准确率:" + accuracy); }
请问训练结果保存在哪里
训练完成之后,可以用knn.Save()保存模型文件。
// 训练
knn.Train(trainData, OpenCvSharp.ML.SampleTypes.RowSample, trainLabel);
// 保存模型
knn.Save(@”img\knn_digits_model.yml”);
能不能分享一下训练好的模型文件呀
参考
c++ OpenCV knn 手写数字识别 模型保存及加载
http://chanpinxue.cn/archives/5511.html
里面从训练、模型保存、模型加载的整个过程写的相对全面。
c++ 虽然与 c# 语法不一样,但思路是一样的。
另外模型的准确性,跟训练参数、训练图片库很有关系的。别人给的模型,不一定有用。
谢谢指导
有一处不太理解,看代码待测试数据也分为0-9存放于文件夹里,这样的话,待检测内容是不是已经假定已知了,如果存放0的文件夹里放上数字1的图片,那识别率不就为0了?
1、训练、测试数据,是为了分析 模型的准确率。
2、实际模型训练完成之后,保存模型。调用模型,再用实际的待检测数据进行检测。
我想问下,如果多次训练,叠加到模型文件中,譬如,第一次用100个图片训练,然后再用新的10个图片训练,保存的两个模型文件怎么叠加呢。
其中一种思路:加载第一次训练完的模型,继续用新的数据训练,生成新的模型。
我想要的效果是一张图一张图去训练,场景是先识别,识别不出或不准时再加载模型文件后再独立训练当前的图片,希望大佬指导下实现方式,谢谢!!
建议查阅一下Yolov5相关的资料。
请教一下,// 声明训练数据集合 mat,60000行,784列,这里的行列和训练图片的数量有什么关系呢?
1、60000行 指的是 60000张 图片。
2、784列 指的是 单张图片【行(28)* 列(28)】,reshape 为 1行 784列。