如何将遗传编程算法应用于可变描述符序列的训练?

21

我目前正在尝试设计一种遗传编程算法,用于分析字符序列为这些字符分配值。下面我举了一个例子集。每行表示一个数据点。训练的值是实数。 例如:对于单词ABCDE,该算法应返回1.0。

示例数据集:

ABCDE : 1

ABCDEF : 10

ABCDEGH : 3

ABCDELKA : 50

AASD : 3

数据集可以尽可能大,因为这只是编造出来的。假设规则不太复杂,并且通过数据进行了说明。

我希望算法能够在给定输入序列时逼近我的数据集中的值。 我现在的问题是,每个序列可能由不同数量的字符组成。如果可能的话,我宁愿不需要自己编写一些花哨的描述符。

我应该如何训练我的GP(最好使用tinyGP或Python)来构建这个模型?

由于这里有很多讨论-图表说出了千言万语: schematics 我想做的就是输入一个数据点并将其放入函数中。 然后我得到一个值,这是我的结果。不幸的是,我不知道这个函数,我只有一些示例数据集(可能只有1000个示例)。现在我使用遗传编程算法来找到一个算法,使其能够将我的数据点转换为结果。 这就是我的模型。 在这种情况下我遇到的问题是,数据点的长度不同。 对于固定长度,我可以将字符串中的每个字符指定为输入参数。但是如果我有不同数量的输入参数该怎么办呢?

免责声明:在我的学习过程中,我多次遇到了这个问题,但我们从未找到一个好的解决方案(比如使用窗口、描述符等等)。我想使用GP,因为我喜欢这项技术并想尝试一下,但在大学期间,我们也尝试过使用ANN等技术,但无济于事。变量输入大小的问题仍然存在。


2
你确定你不想要机器学习中的分类吗? - adrianp
6
从你的问题中并不清楚输入是什么(一个可以分解成“单词”的字符序列?一个“单词”的列表?),也不清楚目标是什么(为个别字符分配值?为“单词”分配值?),因此如何从一个计算另一个几乎无法辨别。 - Scott Hunter
3
遗传算法是一种优化算法。它们主要用于确定组合优化问题的相对最优解,其中直接穷举每个组合是不可行的。不清楚您如何定义解决方案?如果是字符串,则如何计算字符串的分数?最佳分数是什么?人工神经网络更适用于分类,而遗传算法更适用于优化。它们被设计来解决不同类别的问题,因此不能仅仅因为你“喜欢”它们而互换使用。 - Xefan
2
如果你无法为一个解决方案描述适应函数,那么你就不能使用遗传算法。 - Xefan
2
你应该澄清你是否真的想设计一个遗传算法(GA)还是一个遗传编程算法(GP),因为你的问题暗示这两个概念是可以互换的,而它们并不是(第2和4段提到了GA)。 - CmdNtrf
显示剩余8条评论
2个回答

9

由于您没有适应函数,您需要将遗传算法视为分类器。因此,您需要想出一种评估单个染色体的方法。正如其他人建议您的那样,这是一个纯粹的分类问题,而不是优化问题,但是如果您仍然想使用遗传算法,下面是一些尝试初步方法的步骤:

您需要:

  1. 一个有效染色体的描述(如何编码)

要使用遗传算法,所有解决方案必须具有相同的长度(还有更高级的可变长度编码方法,但我不会涉及到那里)。因此,有了这一点,您需要找到一种最佳的编码方法。知道您的输入是一个可变长度的字符串,您可以将染色体编码为字母表的查找表(在Python中为字典)。但是,当您尝试应用交叉或突变操作时,字典会给您带来一些问题,因此最好将字母表和染色体编码拆分。参考语言模型,您可以检查n-grams,并且您的染色体将具有与字母表长度相同的长度:

.. Unigrams

alphabet = "ABCDE"
chromosome1 = [1, 2, 3, 4, 5]
chromosome2 = [1, 1, 2, 1, 0]

.. 二元组

alphabet = ["AB", "AC", "AD", "AE", "BC", "BD", "BE", "CD", "CE", "DE"]
chromosome = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

.. 三元组

alphabet = ["ABC", "ABD", "ABE"...]
chromosome = as above, a value for each combination

2. 解码染色体以评估单个输入

您的染色体将为字母表中的每个元素表示一个整数值。因此,如果您想知道具有染色体的一个输入(可变长度字符串)的值,您将需要尝试一些评估函数,最简单的是每个字母值的总和。

alphabet = "ABC"
chromosome = [1, 2, 1]
input = "ABBBC"

# acc = accumulated value
value = reduce(lambda acc, x: acc + chromosme[alphabet.index(x)], input, 0)
# Will return ABBBC = 1+2+2+2+1 = 8

3. 适应函数

你的适应函数只是一个简单的误差函数。你可以使用简单的误差和,平方误差... 单个基因的简单评估函数:

def fitnessFunction(inputs, results, alphabet, chromosome):
    error = 0

    for i in range(len(inputs)):
        value = reduce(lambda acc, x: acc + chromosome[alphabet.index(x)], inputs[i], 0) 
        diff = abs(results[i] - value)
        error += diff # or diff**2 if you want squared error

    return error

# A simple call -> INPUTS, EXPECTED RESULTS, ALPHABET, CURRENT CHROMOSOME
fitnessFunction(["ABC", "ABB", "ABBC"], [1,2,3], "ABC", [1, 1, 0])
# returned error will be:
# A+B+C = 1 + 1 + 0 -- expected value = 1 --> error += 1
# A+B+B = 1 + 1 + 1 -- expected value = 2 --> error += 1
# A+B+C = 1 + 1 + 1 + 0 -- expected value = 3 --> error += 0
# This chromosome has error of 2

现在,你可以使用任何交叉和变异运算符(例如:单点交叉和位翻转变异),找到使误差最小的染色体。

以下是改善算法模型的尝试:

  • 使用bigrams或trigrams
  • 更改评估方法(当前是查找表值的总和,可以是乘积或更复杂的东西)
  • 尝试在染色体中使用实数而不仅仅是整数

1
很好,@iluengo 给出了一个扎实的答案。不错。+1 - Jonathan
我很欣赏你在回答中所付出的努力,但是这个问题的重点在于使用可变长度编码的高级方法,而这正是你没有涉及到的。如果输入是固定长度的话,我可以直接使用简单的GP算法。 - tarrasch
好的,我将答案分成两条评论,因为它不适合一条。 我没进入那里,因为那不是问题的重点。我的意思是说,变长染色体只是编码相同问题的另一种方式。例如:[3, 2]表示[1,1,1,0,0],而[1,2,1]表示[1,0,0,1](1和0的数量)。 我想说的是,变长染色体只是为您的遗传算法添加了另一个代码解码步骤,它并没有解决您的主要问题,即您不知道适应度函数和染色体的含义。 - Imanol Luengo
就像我一开始说的一样,这是一个分类问题。分类器可以为给定的条目预测一个值,使您成为"黑盒函数"..但在GA中没有黑盒,您需要确切地知道您正在尝试优化的函数。这就是为什么我给你一个初步的方法来尝试猜测适应度函数。但是,如果没有一种编码和评估染色体的方法,在GA中很难工作。这里有一个变长染色体的示例:http://stackoverflow.com/questions/10706586/can-i-have-a-variable-length-chromosome-in-jgap 这只是编码染色体的另一种方式。 - Imanol Luengo
你需要理解的是,我们所讨论的并不是GA(遗传算法),而是GP(遗传程序算法)。这两者有很大的不同。适应函数必须从数据中演化出来,这就是为什么它不能成为输入的一部分。适应函数会使f('aaa')=1。对于未被数据描述的值,算法必须进行外推——这就是GP算法的结果。 - tarrasch
好的,现在一切清楚了。看来我只是搞混了概念,认为GA和GP是相同的。这就是为什么我试图给你一个经典的GA方法。如果我的回答让你感到困惑,对此我很抱歉,也很抱歉浪费了你的时间。至少我学到了一些新东西 =P - Imanol Luengo

5

传统的遗传编程不适用于变长输入。

我认为这个问题中预设了一种评估模型。

例如,假设您将变长输入编码为一个单独的任意精度值,例如使用10个符号的字母表:

ABCD = 1234; ABCDEF = 123456

或者
ABCD = 0.1234; ABCDEF = 0.123456

然而,如果这种编码方式不符合问题域的自然规律,那么编写一个能够处理这种输入的程序将会非常困难。

你也可以假设问题可以通过基因派生的有限状态机来充分表示:

F(F(F(F(init(), A), B), C), D) = 1234

这是一个与基因编程无关的研究领域,你可以在互联网上搜索,阅读研究论文,也许你能找到一个能够满足你需要的程序包。

另一方面,您的问题可能最好由另一个转换来表示,例如二元组的频率--这种转换是有限长度的:

# bigrams
# ABCDE => 1
"AA": 0
"AB": 0.25
"AC": 0
"AD": 0
"AE": 0
"BA": 0
"BC": 0.25
"BD": 0
#... up to end of alphabet ...

(0, 0.25, 0, 0, 0, 0, 0.25, 0, ...., 0, ...) => 1      # ABCDE
(0, 0.20, 0, 0, 0, 0, 0.20, 0, ...., 0.20, ...) => 10  # ABCDEF
# input length N^2

# trigrams
(0, 0.33, 0, 0, ..., 0, ...) => 1      # ABCDE
(0, 0.25, 0, 0, ..., 0.25, ...) => 10  # ABCDEF
# input length N^3

大二元组、三元组等意外地是良好的预测器:
  • 捕获马尔科夫信息(“ab”与“ac”之间的区别)
  • 捕获相对位置(“ab”和“bc”与“ed”和“bc”的比较)
  • 捕获非线性语义(“abab”与“ab”*2不同)
  • 抵抗洗牌输入(“buy new spam”与“buy spam it's new”的比较)

这些经常用于自然语言问题,如文本主题检测、作者检测、垃圾邮件保护;生物技术,如DNA和RNA序列等。

但是,并不能保证这种方法适用于您的问题。这实际上取决于您的问题领域,例如在算术领域中考虑字母表10+,以下两个输入变得无法区分,但产生不同的结果:

10000+10000 = 20000
1000+100000 = 101000

在这种情况下,您需要类似于注册机的东西:
init: tmp = 0; res = 0
"0": tmp *= 10
"1": tmp *= 10; tmp += 1
"+": res += tmp; tmp = 0
end: res += tmp

你能引用一些处理可变大小输入/可变数量描述符的研究论文吗? - tarrasch
1
液态机器(带衰减的神经网络)与遗传编程相结合。http://en.wikipedia.org/wiki/Recurrent_neural_network 也很有趣,虽然不是遗传编程本身。http://www.cs.utsa.edu/~bylander/pubs/AppliedSoftComputing.pdf将时间序列分解为高阶分量。 - Dima Tisnek
Liquid State Genetic Programming的旧链接不再可用。您现在可以从以下链接阅读:https://www.researchgate.net/publication/221157270_Liquid_State_Genetic_Programming - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接