背景
识别素数似乎不适合(人工)神经网络。但是,通用逼近定理指出,神经网络可以逼近任何连续函数,因此特别应该可以表示一个人想要的任何有限支持的函数。因此,让我们尝试识别前百万个数字中的所有质数。
更准确地说,因为这是一个编程网站,所以我们将其设为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)(并且仅使用稍微不同的偏差)。但是,大小超过一百万,因为重复对分数没有任何帮助。
最后,将一个称为激活函数的非线性函数逐项应用到该仿射线性函数的结果。为了这个狭窄的定义,允许的激活函数是ReLU,tanh和Sigmoid。整个层必须使用相同的激活功能。
在基准示例中,权重的数量为20 * 82025 + 82025 * 1 = 1722525,偏差的数量为82025 + 1 = 82026,总得分为1722525 + 82026 =1804551。作为一个示例,如果存在再增加一层,然后将层大小改为[20,a,b,1],则权重数将为20 * a + a * b + b * 1,偏差数将为a + b + 1。
神经网络的这一定义得到了许多框架的充分支持,包括Keras,scikit-learn和Tensorflow。在上面的基准示例中使用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上发布的任何内容。