使用 VB.NET 和遗传算法进行数据分类






4.95/5 (11投票s)
一个使用遗传算法和 VB.Net 对数据进行分类的简单示例
引言
遗传算法是人工智能 (AI) 的一部分。实际上,遗传算法的概念是自然界进化的复制。具有更高一致性和兼容性的新一代可以超越老一代,它们与最接近的世代交换其特征。本文展示了遗传算法如何帮助我们根据一个称为遗传算法函数的函数对数据进行分类。
想象一下,我们有一些数据,这些数据是来自行为相同的区域的不同数据组的组合。这意味着,尽管每个数据组都有其自己的功能,但差异仅在于功能参数之间。下图显示了 XY 坐标上的许多点。显然,我们的数据是两个数据组的组合。现在让我们想象一个条件,在这个条件下我们已经知道公式,并且每个组都是由 y=ax+b 函数生成的。在这个例子中,我们不会在分类中遇到严重问题,因为只有两个组并且数据清晰分离。
在更复杂的示例中,我们无法遵循此类过程。例如,考虑在 1000 个数据集中有五个不同数据组的条件,这些数据组分散在 XY 坐标中或两个或更多数据组的交叉区域。在这里,人类观察不足以进行数据分类。如果我们尝试找出不同数据组和函数的均方误差 (MSE),我们只是浪费时间,因为在大多数情况下,需要计算数十亿个数学项。这就是我们在此类问题中需要遗传算法的地方。
背景
本文不涵盖所有遗传算法主题,而只是一个如何根据现有相关性对不同数据进行分类的应用示例。让我们考虑一个数据组索引数组。该数组的长度等于数据集计数,并且对于每个数据集(X 和 Y),它在其元素内部都有自己的组索引。例如,在上面的图中,我们可能有一个由 0 和 1 组成的数组,因为假设我们只有两个数据组。
这样的数组可以以不同的形式出现,每种形式都根据我们遗传算法函数(这里是 y=ax+b)的计算参数具有自己的 MSE。下图显示了这种数组的十种不同形式。一开始我们随机设置值。现在我们的遗传算法开始了。
首先,算法应该计算上面所有十种情况的 MSE。我们将这些情况称为遗传算法字符串。显然,每个字符串都有自己的 MSE。计算误差后,我们现在可以猜测更接近我们最终分类的字符串。这里我们将字符串从最佳 MSE 到最差 MSE 进行排序。事实上,每个猜测的索引都有另一个 MSE——它本身——在字符串内部。遗传算法的进化取决于更好地选择要交换的元素。换句话说,为了选择最佳元素(要交换),我们必须知道它的 MSE。对于此类 MSE 的最高值,我们可以说当前元素未正确设置,因此它的值可以与附近字符串中具有相似特征的每个元素中的值交换。
因此,在下一步中,我们开始交换字符串的高内部 MSE 元素,以便每个字符串与其相邻的字符串交换值。这就是我们的字符串计数是偶数的原因。要交换的元素数量可以随机设置,但为了获得更可靠的结果和更稳定的算法,最好在每个循环中进行较少次数的交换。在上图中,我们在前两个字符串中有两次交换,对于其他字符串对,这个数字可能不同。这里我们的第一个循环完成。现在我们必须通过重新计算 MSE 来重新启动该过程。因此,一个循环中的过程包括三个步骤
- 计算字符串及其元素的 MSE
- 根据 MSE 排序字符串
- 交换高误差元素值
请记住,这种算法只是可以利用的数十种算法之一。我们有更多的方法和技巧可以加快收敛速度,但这个例子足够简单明了,易于理解和应用。
Using the Code
首先,我们必须为数据处理声明合适的数据结构。
输入数据结构
Structure DataGroup
Dim X() As Double
Dim Y() As Double
End Structure
遗传字符串结构
Structure DataGroup
Dim X() As Double
Dim Y() As Double
End Structure
回归参数结构
为了评估 MSE,我们必须将点拟合到 GA
函数。此拟合过程的初步结果是回归参数。在这种情况下,我们假设函数是 y=ax+b,因此参数是 a 和 b。
Structure RegressionParameter
Dim a As Double
Dim b As Double
End Structure
输出结构
显然,我们需要在每个循环中检查 GA
的响应。
Structure OutPut
Dim RP() As RegressionParameter
Dim BestString As GeneticString
Dim MSE As Double
End Structure
现在可以声明属性
属性 | 类型 | 描述 |
纪元 |
整数 | 最大纪元数 |
MSE |
双精度浮点型 | 所需 MSE |
字符串计数 |
Short | 算法中的字符串数量 |
遗传字符串() |
_遗传字符串 | GA 字符串数组 |
数据组 |
_数据组 | 输入数据 |
输出 |
_输出 | GA 结果 |
还声明了两个事件:Epochs
和 Goalmet
。它们的名称一目了然。每次循环完成时都会执行 Epochs
事件,当达到最大纪元或误差低于所需 MSE 时将调用 Goalmet
。
根据前面提到的三个步骤,我们可以将我们的子例程和函数分为三组。
计算字符串及其元素的 MSE
Sub CalculateBitError(ByRef GString As _GeneticString,
ByVal RParameter() As _RegressionParameter)
Function ReturnMSE(ByVal DGroup() As _DataGroup, ByVal RP() As _RegressionParameter)
Function FindPointMSE(ByVal X As Double, ByVal Y As Double,
ByVal RP As _RegressionParameter) As Double
Function FindMSE(ByVal X() As Double, ByVal Y() As Double,
ByVal RP As _RegressionParameter) As Double
Function PerformRegression(ByVal DGroup() As _DataGroup,
ByRef RP() As _RegressionParameter) As Boolean
Function LinearRegression(ByVal X() As Double, ByVal Y() As Double,
ByRef b As Double, ByRef a As Double) As Boolean
根据 MSE 排序字符串
Sub Sort(ByRef GString() As _GeneticString)
Sub Switch(ByRef Val1 As Double, ByRef Val2 As Double)
交换高误差元素值
Sub BitExchange(ByRef STC() As _GeneticString)
Sub Exchange(ByRef STC1 As _GeneticString, ByRef STC2 As _GeneticString,
ByVal num As Integer)
Function PrepareDataGroup(ByVal LineCount As Integer, ByRef GString As _GeneticString,
ByVal X() As Double, ByVal Y() As Double) As _DataGroup()
上述大多数子例程和函数的作用很明确。所有这些代码都在 GA
类中,因此我们必须实例化这个类。
单击“分类”按钮将执行以下代码。这里,GetXY
子例程返回 DataGridView 中的 X
和 Y
值。
Dim DG As New GA._DataGroup
GetXY(DG.X, DG.Y)
With GA
.DataGroup = DG
.StringCount = nud1.Value
.Epoch = TextBox1.Text
.MaxMSE = TextBox2.Text
.Run()
End With
结果将保存在 GA 的 Output
属性中,并可以从 GoalMet
事件处理程序中读取。最佳 GA 字符串——一个由 0 和 1 组成的数组——可以用来表示我们用不同颜色进行的分类,如下所示。就这样!
Copyright
源代码完全免费。一个名为 IMP 的控件用于绘图。该控件正在开发中并且存在错误。我不建议您在项目中使用 IMP。
关注点
对于更复杂的数据集,例如交集(如下图所示),最好有更高的纪元数。更高的字符串计数可以帮助我们获得稳定的算法,但两者都会降低收敛速度。
该代码用 VB.NET 编写,仅作为在此类研究案例中编写遗传算法的指南。如果代码是用 .NET 语言编写的,则不适合繁重的数学任务。最好用编译速度更快的低级语言重新编写代码。