C# 中的深度学习:预处理硬币检测数据集





5.00/5 (4投票s)
在下一篇文章中,我们将预处理一个数据集,以便输入到机器学习模型中。
- 清理数据 - 通过平均周围数据的值或使用其他策略来填补缺失或损坏的数据留下的空白。
- 归一化数据 - 将值缩放到标准范围,通常为 0 到 1。具有广泛取值范围的数据会导致不规则性,因此我们将所有内容都放入一个共同的范围。
- One Hot 编码标签 - 将数据集中对象的标签或类别编码为二进制 N 维向量,其中 N 是类别的总数。数组元素全部设置为 0,只有与对象类别对应的元素设置为 1。这意味着在每个数组中,只有一个元素的值为 1。
- 将输入数据集分成训练集和验证集 - 训练集用于训练模型,验证集用于通过评估训练后的模型(针对未在原始数据集上训练过的子集)来检查我们的训练准确度。
在本例中,我们将使用 Numpy.NET,它基本上是 Python 中流行的 Numpy 库的 .NET 版本。 Numpy 是一个专注于处理矩阵的库。
为了实现我们的数据集处理器,我们在 PreProcessing 文件夹中创建了 Utils
和 DataSet
类。 Utils
类包含一个静态 Normalize
方法,如下所示
public class Utils
{
public static NDarray Normalize(string path)
{
var colorMode = Settings.Channels == 3 ? "rgb" : "grayscale";
var img = ImageUtil.LoadImg(path, color_mode: colorMode, target_size: (Settings.ImgWidth, Settings.ImgHeight));
return ImageUtil.ImageToArray(img) / 255;
}
}
在此方法中,我们使用给定的颜色模式(RGB 或灰度)加载图像,并将其大小调整为给定的宽度和高度。然后,我们返回包含图像的矩阵,其中每个元素都除以 255。将每个元素除以 255 会对其进行归一化,因为图像中任何像素的值都在 0 到 255 之间,因此通过将它们除以 255,我们确保新范围为 0 到 1,包括 0 和 1。
我们还在代码中使用了一个 Settings
类。此类包含在整个应用程序中使用的许多参数的可能值的常量。另一个类 DataSet
表示我们将用于训练机器学习模型的数据集。这里我们有以下字段
_pathToFolder
- 包含图像的文件夹的路径。_extList
- 要考虑的文件扩展名列表。_labels
-_pathToFolder
中图像的标签或类别。_objs
- 图像本身,表示为Numpy.NDarray
。_validationSplit
- 用于将图像总数划分为验证集和训练集的百分比,在本例中,该百分比将定义验证集相对于图像总数的大小。NumberClasses
- 数据集中唯一类别的总数。TrainX
- 训练数据,表示为Numpy.NDarray
。TrainY
- 训练标签,表示为Numpy.NDarray
。ValidationX
- 验证数据,表示为Numpy.NDarray
。ValidationY
- 验证标签,表示为Numpy.NDarray
。
这是 DataSet
类
public class DataSet
{
private string _pathToFolder;
private string[] _extList;
private List<int> _labels;
private List<NDarray> _objs;
private double _validationSplit;
public int NumberClasses { get; set; }
public NDarray TrainX { get; set; }
public NDarray ValidationX { get; set; }
public NDarray TrainY { get; set; }
public NDarray ValidationY { get; set; }
public DataSet(string pathToFolder, string[] extList, int numberClasses, double validationSplit)
{
_pathToFolder = pathToFolder;
_extList = extList;
NumberClasses = numberClasses;
_labels = new List<int>();
_objs = new List<NDarray>();
_validationSplit = validationSplit;
}
public void LoadDataSet()
{
// Process the list of files found in the directory.
string[] fileEntries = Directory.GetFiles(_pathToFolder);
foreach (string fileName in fileEntries)
if (IsRequiredExtFile(fileName))
ProcessFile(fileName);
MapToClassRange();
GetTrainValidationData();
}
private bool IsRequiredExtFile(string fileName)
{
foreach (var ext in _extList)
{
if (fileName.Contains("." + ext))
{
return true;
}
}
return false;
}
private void MapToClassRange()
{
HashSet<int> uniqueLabels = _labels.ToHashSet();
var uniqueLabelList = uniqueLabels.ToList();
uniqueLabelList.Sort();
_labels = _labels.Select(x => uniqueLabelList.IndexOf(x)).ToList();
}
private NDarray OneHotEncoding(List<int> labels)
{
var npLabels = np.array(labels.ToArray()).reshape(-1);
return Util.ToCategorical(npLabels, num_classes: NumberClasses);
}
private void ProcessFile(string path)
{
_objs.Add(Utils.Normalize(path));
ProcessLabel(Path.GetFileName(path));
}
private void ProcessLabel(string filename)
{
_labels.Add(int.Parse(ExtractClassFromFileName(filename)));
}
private string ExtractClassFromFileName(string filename)
{
return filename.Split('_')[0].Replace("class", "");
}
private void GetTrainValidationData()
{
var listIndices = Enumerable.Range(0, _labels.Count).ToList();
var toValidate = _objs.Count * _validationSplit;
var random = new Random();
var xValResult = new List<NDarray>();
var yValResult = new List<int>();
var xTrainResult = new List<NDarray>();
var yTrainResult = new List<int>();
// Split validation data
for (var i = 0; i < toValidate; i++)
{
var randomIndex = random.Next(0, listIndices.Count);
var indexVal = listIndices[randomIndex];
xValResult.Add(_objs[indexVal]);
yValResult.Add(_labels[indexVal]);
listIndices.RemoveAt(randomIndex);
}
// Split rest (training data)
listIndices.ForEach(indexVal =>
{
xTrainResult.Add(_objs[indexVal]);
yTrainResult.Add(_labels[indexVal]);
});
TrainY = OneHotEncoding(yTrainResult);
ValidationY = OneHotEncoding(yValResult);
TrainX = np.array(xTrainResult);
ValidationX = np.array(xValResult);
}
}
这是对每个方法的解释
LoadDataSet()
- 类的主要方法,我们调用它来加载_pathToFolder
中的数据集。它调用下面列出的其他方法来执行此操作。IsRequiredExtFile(filename)
- 检查给定文件是否包含至少一个应该为此数据集处理的扩展名(在_extList
中列出)。MapToClassRange()
- 获取数据集中唯一标签的列表。ProcessFile(path)
- 使用Utils.Normalize
方法来归一化图像并调用ProcessLabel
方法。ProcessLabel(filename)
- 将ExtractClassFromFileName
方法的结果添加为标签。ExtractClassFromFileName(filename)
- 从图像的文件名中提取类别。GetTrainValidationData()
- 将数据集划分为训练和验证子数据集。
在本系列中,我们将使用硬币图像数据集,网址为 https://cvl.tuwien.ac.at/research/cvl-databases/coin-image-dataset/。
要加载数据集,我们可以在控制台应用程序的主类中包含以下内容
var numberClasses = 60;
var fileExt = new string[] { ".png" };
var dataSetFilePath = @"C:/Users/arnal/Downloads/coin_dataset";
var dataSet = new PreProcessing.DataSet(dataSetFilePath, fileExt, numberClasses, 0.2);
dataSet.LoadDataSet();
我们的数据现在可以输入到机器学习模型中了。 下一篇文章 将介绍监督式机器学习的基础知识,以及训练和验证阶段的组成部分。它适用于几乎没有人工智能经验的读者。