神经网络可以识别素数吗?


26

背景

识别素数似乎不适合(人工)神经网络。但是,通用逼近定理指出,神经网络可以逼近任何连续函数,因此特别应该可以表示一个人想要的任何有限支持的函数。因此,让我们尝试识别前百万个数字中的所有质数。

更准确地说,因为这是一个编程网站,所以我们将其设为2 ^ 20 = 1,048,576。低于此阈值的质数为82,025或大约8%。

挑战

您能找到将神经网络正确分类为素数或不素数的20个整数吗?

出于此挑战的目的,神经网络的大小是表示它所需的权重和偏差的总数。

细节

目标是最小化单个显式神经网络的大小。

您网络的输入将是一个长度为20的矢量,其中包含整数的各个位,分别用0和1或-1和1表示。这些的顺序可以是最高有效位在前或最低有效位在前。

网络的输出应为单个数字,以便在某个截止值以上将输入识别为质数,而在同一截止值以下则将输入视为非质数。例如,正数可能表示素数(负数不是素数),或者大于0.5可能意味着素数(小于0.5则不是素数)。

在所有2 ^ 20 = 1,048,576个可能的输入上,网络必须是100%准确的。如上所述,请注意,此范围内有82,025个素数。(因此,始终输出“非素数”的精度为92%。)

用标准的神经网络术语来说,这可能称为过拟合。换句话说,您的目标是完美拟合素数。可能使用的其他词语是“训练集”和“测试集”相同。

该挑战不考虑“可训练”或“可学习”参数的数量。确实,您的网络可能包含硬编码的权重,下面的示例完全是硬编码的。取而代之的是,所有的重量和偏见是考虑的参数和计数。

训练或生成您的神经网络所需的代码长度与您的分数无关,但是发布相关代码当然值得赞赏。

基准线

作为基准,可以“记住”所有82,025个素数,总重量和偏差为1,804,551

请注意,下面的代码包含许多内容:工作示例,工作测试代码,使用已知神经网络库的神经网络的有效定义,“硬编码”(或至少不是“训练有素”)神经网络,和分数的有效衡量。

import numpy as np

bits = 20

from keras.models import Sequential
from keras.layers import Dense

from sympy import isprime

# Hardcode some weights
weights = []
biases  = []
for n in xrange(1<<bits):
    if not isprime(n):
        continue
    bit_list = [(n / (1 << i))%2 for i in xrange(bits)]
    weight = [2*bit - 1 for bit in bit_list]
    bias   = - (sum(bit_list) - 1)
    weights.append(weight)
    biases .append(bias)
nprimes = len(biases)
weights1 = np.transpose(np.array(weights))
biases1  = np.array(biases )
weights2 = np.full( (nprimes,1), 1 )
biases2  = np.array( [0] )

model = Sequential()
model.add(Dense(units=nprimes, activation='relu', input_dim=bits, weights=[weights1, biases1]))
model.add(Dense(units=1, activation='relu', weights=[weights2, biases2]))
print "Total weights and biases: {}".format( np.size(weights1) + np.size(weights2) + np.size(biases1) + np.size(biases2) )

# Evaluate performance
x = []
y = []
for n in xrange(1<<bits):
    row = [(n / (1 << i))%2 for i in xrange(bits)]
    x.append( row )
    col = 0
    if isprime(n):
        col = 1
    y.append( col )
x = np.array(x)
y = np.array(y)

model.compile(loss='binary_crossentropy', optimizer='sgd', metrics=['accuracy'])

loss, accuracy = model.evaluate(x, y, batch_size=256)
if accuracy == 1.0:
    print "Perfect fit."
else:
    print "Made at least one mistake."

什么是神经网络?

出于这一挑战的目的,我们可以写下(人工)神经网络的狭窄但精确的定义。对于一些外部阅读,我建议维基百科使用人工神经网络前馈神经网络多层感知器激活功能

一个前馈神经网络是一家集的神经元。每层神经元的数量各不相同,输入层有20个神经元,一个或多个隐藏层有一定数量的神经元,输出层有1个神经元。(必须至少有一个隐藏层,因为素数和非素数不能根据它们的位模式线性分离。)在上述基准示例中,层的大小为[20,82025,1]。

输入神经元的值由输入确定。如上所述,这将是对应于介于0和2 ^ 20之间的数字的位的0和1,或者类似地是-1和+1。

包括输出层在内的每个随后层的神经元的值都是事先从该层确定的。首先,以完全连接密集的方式应用线性函数。表示这种函数的一种方法是使用权重矩阵。例如,基线的前两层之间的转换可用82025 x 20矩阵表示。权数是此矩阵中条目的数量,例如1640500。然后,每个条目都添加了一个(单独的)偏差项。这可以用向量表示,例如在我们的例子中是82025 x 1矩阵。偏差数是条目数,例如82025。(请注意,权重和偏差一起描述了仿射线性函数。)

即使权重或偏差为零,也要进行计数。为了这个狭义的定义,即使偏差全为零,偏差也计为权重。请注意,在基线示例中,仅使用两个不同的权重(+1和-1)(并且仅使用稍微不同的偏差)。但是,大小超过一百万,因为重复对分数没有任何帮助。

最后,将一个称为激活函数的非线性函数逐项应用到该仿射线性函数的结果。为了这个狭窄的定义,允许的激活函数是ReLUtanhSigmoid。整个层必须使用相同的激活功能。

在基准示例中,权重的数量为20 * 82025 + 82025 * 1 = 1722525,偏差的数量为82025 + 1 = 82026,总得分为1722525 + 82026 =1804551。作为一个示例,如果存在再增加一层,然后将层大小改为[20,a,b,1],则权重数将为20 * a + a * b + b * 1,偏差数将为a + b + 1。

神经网络的这一定义得到了许多框架的充分支持,包括Kerasscikit-learnTensorflow。在上面的基准示例中使用Keras,其代码基本上如下:

from keras.models import Sequential
model = Sequential()
from keras.layers import Dense
model.add(Dense(units=82025, activation='relu', input_dim=20, weights=[weights1, biases1]))
model.add(Dense(units=1, activation='relu', weights=[weights2, biases2]))
score = numpy.size(weights1) + numpy.size(biases1) + numpy.size(weights2) + numpy.size(biases2)

如果权重和偏差矩阵是numpy数组,则numpy.size将直接告诉您条目数。

还有其他种类的神经网络吗?

如果您想要一个简单,精确的神经网络定义和评分来解决此挑战,请使用上一节中的定义。如果您认为“正确看待任何函数”是没有参数的神经网络,那么请使用上一节中的定义。

如果您比较自由,那么我鼓励您进一步探索。也许您的答案将不会计入狭窄的挑战,但也许您会获得更多乐趣。您可以尝试的其他一些想法包括更多奇异的激活函数,递归神经网络(一次读取一位),卷积神经网络,更多奇异的体系结构,softmax和LSTM(!)。您可以使用任何标准激活功能和任何标准体系结构。“标准”神经网络功能的宽松定义可以包括在发布此问题之前在arxiv上发布的任何内容。


这些砝码是哪种类型?通常人们使用浮点数,我们可以使用其他数字类型吗?例如,更少,更多或无限精度的类型。
小麦向导

@ SriotchilismO'Zaic:出于狭义的定义,我认为限制所有权重和中间值的浮点数和双数(IEEE单精度和双精度浮点实数)是有意义的。(尽管请注意,某些实现可能会在评估过程中使用其他精度级别(例如80位)。)
A. Rex

我喜欢这个问题,但很失望,没有足够的训练时间可以找到更小的神经网络。
阿努什

Answers:


13

试验师:得分59407、6243层,总共16478个神经元

作为生成和验证网络的Python程序给出。请参阅中的评论以trial_division获取有关其工作原理的解释。验证速度很慢(例如,运行时间以小时为单位):我建议使用PyPy或Cython。

α最高0α

阈值为1:高于质数的阈值,低于质数的复合值或零,并且输入为1的唯一输入本身就是1。

#!/usr/bin/python3

import math


def primes_to(n):
    ps = []
    for i in range(2, n):
        is_composite = False
        for p in ps:
            if i % p == 0:
                is_composite = True
                break
            if p * p > i:
                break
        if not is_composite:
            ps.append(i)
    return ps


def eval_net(net, inputs):
    for layer in net:
        inputs.append(1)
        n = len(inputs)
        inputs = [max(0, sum(inputs[i] * neuron[i] for i in range(n))) for neuron in layer]
    return inputs


def cost(net):
    return sum(len(layer) * len(layer[0]) for layer in net)


def trial_division(num_bits):
    # Overview: we convert the bits to a single number x and perform trial division.
    # x is also our "is prime" flag: whenever we prove that x is composite, we clear it to 0
    # At the end x will be non-zero only if it's a unit or a prime, and greater than 1 only if it's a prime.
    # We calculate x % p as
    #     rem = x - (x >= (p << a) ? 1 : 0) * (p << a)
    #     rem -= (rem >= (p << (a-1)) ? 1) : 0) * (p << (a-1))
    #     ...
    #     rem -= (rem >= p ? 1 : 0) * p
    #
    # If x % p == 0 and x > p then x is a composite multiple of p and we want to set it to 0

    N = 1 << num_bits
    primes = primes_to(1 + int(2.0 ** (num_bits / 2)))

    # As a micro-optimisation we exploit 2 == -1 (mod 3) to skip a number of shifts for p=3.
    # We need to bias by a multiple of 3 which is at least num_bits // 2 so that we don't get a negative intermediate value.
    bias3 = num_bits // 2
    bias3 += (3 - (bias3 % 3)) % 3

    # inputs: [bit0, ..., bit19]
    yield [[1 << i for i in range(num_bits)] + [0],
           [-1] + [0] * (num_bits - 1) + [1],
           [0] * 2 + [-1] * (num_bits - 2) + [1],
           [(-1) ** i for i in range(num_bits)] + [bias3]]

    for p in primes[1:]:
        # As a keyhole optimisation we overlap the cases slightly.
        if p == 3:
            # [x, x_is_even, x_lt_4, x_reduced_mod_3]
            max_shift = int(math.log((bias3 + (num_bits + 1) // 2) // p, 2))
            yield [[1, 0, 0, 0, 0], [0, 1, -1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, -1, p << max_shift]]
            yield [[1, -N, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, 1]]
            yield [[1, 0, 0, 0], [0, 1, -p << max_shift, 0]]
        else:
            # [x, x % old_p]
            max_shift = int(num_bits - math.log(p, 2))
            yield [[1, 0, 0], [1, -N, -p_old], [-1, 0, p << max_shift]]
            yield [[1, -N, 0, 0], [0, 0, -1, 1]]
            yield [[1, 0, 0], [1, -p << max_shift, 0]]

        for shift in range(max_shift - 1, -1, -1):
            # [x, rem]
            yield [[1, 0, 0], [0, 1, 0], [0, -1, p << shift]]
            yield [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 1]]
            yield [[1, 0, 0, 0], [0, 1, -p << shift, 0]]
        # [x, x % p]
        p_old = p

    yield [[1, 0, 0], [1, -N, -p]]
    yield [[1, -N, 0]]


def validate_primality_tester(primality_tester, threshold):
    num_bits = len(primality_tester[0][0]) - 1
    primes = set(primes_to(1 << num_bits))
    errors = 0
    for i in range(1 << num_bits):
        expected = i in primes
        observed = eval_net(primality_tester, [(i >> shift) & 1 for shift in range(num_bits)])[-1] > threshold
        if expected != observed:
            errors += 1
            print("Failed test case", i)
        if (i & 0xff) == 0:
            print("Progress", i)

    if errors > 0:
        raise Exception("Failed " + str(errors) + " test case(s)")


if __name__ == "__main__":
    n = 20

    trial_div = list(trial_division(n))
    print("Cost", cost(trial_div))
    validate_primality_tester(trial_div, 1)

顺便说一句

通用逼近定理指出,神经网络可以逼近任何连续函数

最高01个-一种一世最高01个+一种一世-1个但只有在保证输入为0或1的情况下才能正常工作,并且可以输出更大的整数。一层中可能有其他各种门,但是NOR本身就是图灵完备的,因此无需赘述。


另外,在尝试进行除法运算之前,我开始进行Euler测试,因为我认为这样会更有效,但是将数字(7是最佳候选数)提高为(x-(x mod 2) )将需要进行38次乘法运算,然后再乘以减少模数x,而我发现用于乘以20位数字的最佳网络的成本为1135,因此它不会具有竞争力。
彼得·泰勒

7

总计984314、82027层,246076个神经元

如果使用激活函数ReLU,我们可以将所有事情完全保留在整数中,从而简化了分析。

XX=一种

  1. ge一种=X-一种+一种=-X+一种+
  2. 当量一种=-ge一种-一种+1个+当量一种1个X=一种0

X使用权重1、2、4 ... ...和偏差0。成本:(20 +1)* 1 = 21。

ge2=X-2+2=-X+2+

积累2=-ge2-2+1个+ge3=ge2-3-2+3=-ge2+3-2+

积累3=221积累2-ge3-3+1个+ge5=ge3-5-3+5=-ge3+5-3+

积累5=221积累3-ge5-5+1个+ge7=ge5-7-5+7=-ge5+7-5+

...

积累1048571=221积累1048559-ge1048571-1048571+1个+ge1048573=ge1048571-1048573-1048571+1048573=-ge1048571+1048573-1048571+

积累1048573=221积累1048571-ge1048573-1048573+1个+

+

得分是(82026-3)* 12 + 21 + 4 + 9 + 4。


凉。据我了解,这也“记住”了素数,但是它“顺序”而不是“并行”地测试相等性。(或者,它就像是基线的转置。)第一步是立即远离位模式,只使用实际的整数本身。结果,在平等检查中不会受到20倍的惩罚。感谢您的提交
A. Rex

什么是上标加?
feersum

1
X+=最高0X
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.