遗传算法是一种模仿自然界进化过程的优化算法。在自然界中,新一代的生物体通过遗传和变异,能够超越老一代,展现出更强的适应性和一致性。本文将探讨如何利用遗传算法来帮助对数据进行分类,特别是基于一个称为遗传算法函数的函数。
设想有一组数据,这些数据由具有相同行为特征的不同数据组组合而成。这意味着,尽管每个数据组都有自己的功能,但差异仅在于函数参数。下图展示了XY坐标上的一些点。显然,数据由两个数据组组成。现在让想象一个条件,已经知道公式,每个组都是由函数y=ax+b生成的。在这个例子中,没有面临严重的分类问题,因为只有两个组,数据清晰地分开了。
在更复杂的例子中,无法遵循这样的程序。例如,假设有1000个数据集中的五个不同的数据组,这些数据组分散在XY坐标或两个或更多数据组的交叉区域。在这里,人类的观察对于数据分类来说不够可靠。如果尝试为不同的数据组和函数找到均方误差(MSE),只是在浪费时间,因为在大多数情况下,需要计算数十亿个数学项。这就是在这类问题中需要遗传算法的地方。
本文不涵盖所有遗传算法的主题,而只是一个应用示例,展示了如何根据可用的相关性对不同数据进行分类。让考虑一个数据组索引的数组。这个数组的长度等于数据集的数量,对于每个数据集(X和Y),它在自己的元素内部有自己的组索引。例如,在上图中,可能有0和1的数组,因为假设只有两个数据组。
这样的数组可以以不同的形式出现,每种形式都有自己基于遗传算法函数(这里y=ax+b)计算参数的MSE。下图展示了这种数组的十种不同形式。一开始随机设置值。现在遗传算法开始了。
首先,算法应该计算所有十个案例的MSE。称这些案例为遗传算法字符串。显然,每个字符串都有自己的MSE。计算错误后,现在可以猜测更接近最终分类的字符串。这里将字符串从最好的MSE到最差的排序。事实上,每个猜测的索引在字符串内部都有另一个MSE。遗传算法的演变取决于更好的元素选择来进行交换。换句话说,为了选择最好的元素(进行交换),需要知道它的MSE。对于这样的MSE最高值,可以说当前元素没有正确设置,因此它的值可以被附近字符串中具有类似特征的每个元素的值交换。
接下来,开始交换字符串中高内部MSE元素的值,以这种方式,每个字符串与其邻域的字符串交换值。这就是字符串数量是偶数的原因。交换元素的数量可以随机设置,但对于更可靠的结果和更稳定的算法,最好在每个循环中进行更少的交换。在上面的图中,在前两个字符串中进行了两次交换,对于其他字符串对,这个数字可以不同。这里完成了第一个循环。现在必须通过重新计算MSEs重新启动程序。因此,一个循环中的程序包括三个步骤:
请记住,这个算法是可以使用的数十种算法之一。有更多更快收敛的方法和技巧,但这个例子足够简单明了,易于理解和应用。
首先,必须声明适合数据处理的数据结构。
Structure DataGroup
Dim X() As Double
Dim Y() As Double
End Structure
Structure GeneticString
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
现在可以声明属性:
属性 | 类型 | 描述 |
---|---|---|
Epoch | Integer | 最大循环次数 |
MSE | Double | 期望的MSE |
StringCount | Short | 算法中的字符串数量 |
还声明了两个事件:Epochs和Goalmet。它们的名字很直接。Epochs事件将在每次循环完成时执行,Goalmet将在达到最大循环次数或错误低于期望MSE时调用。
根据上述三个步骤,可以将子程序和函数分为三组。
Sub CalculateBitError(ByRef GString As _GeneticString, ByVal RParameter() As _RegressionParameter)
Function ReturnMSE(ByVal DGroup() As _DataGroup, ByVal RP() As _RegressionParameter) As Double
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
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的数组——可以用来用不同的颜色表示分类,如下所示。就是这样!