신경망이 소수를 인식 할 수 있습니까?


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 % 정확해야합니다.

표준 신경망 용어의 관점에서이를 초과 적합 이라고 합니다 . 다시 말해, 목표는 소수를 완벽하게 초과 맞추는 것입니다. 다른 말로 "트레이닝 세트"와 "테스트 세트"가 동일하다는 것입니다.

이 과제는 "훈련 가능"또는 "학습 가능"매개 변수의 수를 고려하지 않습니다. 실제로 네트워크에는 하드 코딩 된 가중치가 포함되어있을 가능성이 있으며 아래 예는 완전히 하드 코딩 된 것입니다. 대신, 모든 가중치와 바이어스는 매개 변수로 간주되고 계산됩니다.

신경망을 훈련 시키거나 생성하는 데 필요한 코드 길이는 점수와 관련이 없지만 관련 코드를 게시하는 것이 좋습니다.

베이스 라인

기본적으로1,804,551 개의 총 중량과 바이어스 로 82,025 개의 프라임을 모두 "암기"할 수 있습니다 .

다음 코드는 작업 예제, 작업 테스트 코드, 알려진 신경망 라이브러리를 사용하는 신경망의 작업 정의, "하드 코딩 된"(또는 "훈련되지 않은"신경망) 신경망, 그리고 점수의 실제 측정.

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."

신경망이란 무엇입니까?

이 도전의 목적을 위해, (인공) 신경망의 좁지 만 정확한 정의를 적을 수 있습니다. 외부 독서의 경우 인공 신경 네트워크 , 피드 포워드 신경 네트워크 , 다층 퍼셉트론활성화 기능 에 대한 Wikipedia를 제안 합니다. .

피드 포워드 신경망 의 모음 뉴런. 레이어 당 뉴런의 수는 입력 레이어에 20 개의 뉴런, 하나 이상의 숨겨진 레이어에있는 일부 뉴런, 출력 레이어에 1 개의 뉴런이 있습니다. (프라임과 비 프라임은 비트 패턴에 따라 선형으로 분리 할 수 ​​없기 때문에 숨겨진 레이어가 하나 이상 있어야합니다.) 위의 기본 예에서 레이어의 크기는 [20, 82025, 1]입니다.

입력 뉴런의 값은 입력에 의해 결정됩니다. 상술 한 바와 같이, 이것은 0과 2 ^ 20 사이의 숫자 비트에 대응하는 0과 1이거나 유사하게 -1과 +1이다.

출력 레이어를 포함하여 각 후속 레이어의 뉴런 값은 미리 레이어에서 결정됩니다. 먼저 선형 기능이 완전히 연결 되거나 조밀 하게 적용됩니다 . 이러한 함수를 나타내는 한 가지 방법은 가중치 행렬을 사용하는 것 입니다. 예를 들어, 기준선의 첫 두 레이어 사이의 전이는 82025 x 20 매트릭스로 표현 될 수 있습니다. 가중치 수는이 행렬의 항목 수 (예 : 1640500)입니다. 그런 다음 각 항목에 (별도의) 바이어스 항이 추가됩니다. 이것은 벡터, 예를 들어 우리의 경우 82025 x 1 행렬로 나타낼 수 있습니다. 바이어스 수는 항목 수 (예 : 82025)입니다. 가중치와 바이어스는 함께 아핀 선형 함수를 나타 냅니다.

가중치 또는 바이어스는 0이더라도 계산됩니다. 이 좁은 정의의 목적을 위해 바이어스는 모두 0 인 경우에도 가중치로 계산됩니다. 기준선 예에서는 두 개의 서로 다른 가중치 (+1 및 -1) 만 사용됩니다 (그리고 약간 더 뚜렷한 바이어스 만 있음). 그럼에도 불구하고, 반복은 점수에 도움이되지 않기 때문에 크기는 백만 이상입니다.

마지막으로, 활성화 함수라고하는 비선형 함수 가이 아핀 선형 함수의 결과에 항목별로 적용됩니다. 이 좁은 정의를 위해 허용되는 활성화 함수는 ReLU , tanhsigmoid 입니다. 전체 레이어는 동일한 활성화 기능을 사용해야합니다.

기준선 예에서 가중치의 수는 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 배우기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에 게시 된 모든 내용이 포함될 수 있습니다.


이 가중치는 어떤 유형입니까? 보통 사람들은 float를 사용합니다. 다른 숫자 형을 사용할 수 있습니까? 예를 들어, 더 적거나 더 많거나 무제한 인 유형.
밀 마법사

@ SriotchilismO'Zaic : 좁은 정의의 목적을 위해 모든 가중치와 중간 값에 대해 float 및 double (IEEE 단 정밀도 및 배정 밀도 부동 소수점 실수)로 제한하는 것이 합리적이라고 생각합니다. (일부 구현에서는 평가 중에 다른 정도의 정밀도 (예 : 80 비트)를 사용할 수도 있습니다.)
A. Rex

나는이 질문을 좋아하지만 충분한 훈련 시간으로 찾을 수있는 훨씬 작은 신경망이 없다는 것에 실망합니다.
아누 쉬

답변:


13

시험 분할 : 점수 59407, 6243 개의 층, 총 16478 개의 뉴런

인터넷을 생성하고 유효성을 검사하는 Python 프로그램으로 제공됩니다. trial_division작동 방식에 대한 설명은 주석을 참조하십시오 . 유효성 검사는 매우 느립니다 (시간 단위로 측정 된 실행 시간). PyPy 또는 Cython을 사용하는 것이 좋습니다.

α최대(0,α)

임계 값은 1입니다. 위의 모든 것은 소수이고, 아래의 것은 복합이거나 0이며, 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)

옆으로, 다시

보편적 근사 정리는 신경망이 어떤 연속적인 기능에 근사 할 수 있다고 명시하고있다

최대(0,1에이나는)최대(0,1+(에이나는1))입력이 0 또는 1로 보장되고 더 큰 정수를 출력 할 수있는 경우에만 올바르게 작동합니다. 한 층에서 다양한 다른 게이트가 가능하지만 NOR 자체는 Turing-complete이기 때문에 자세히 설명 할 필요가 없습니다.


더 나아간 것으로, 시험 분할을 시도하기 전에 오일러 테스트 작업을 시작했습니다. 왜냐하면 더 효율적이라고 생각했지만 숫자 (7이 가장 좋은 후보였습니다)를 (x- (x mod 2)의 거듭 제곱으로 올렸습니다. )에는 38 곱셈과 축소 mod x가 필요하며 20 비트 숫자를 곱하는 데 가장 적합한 네트워크 비용은 1135이므로 경쟁력이 없습니다.
피터 테일러

7

점수 984314, 82027 층, 총 246076 뉴런

활성화 함수 ReLU를 사용하면 모든 것을 정수로 유지할 수있어 분석이 간단 해집니다.

엑스엑스=에이

  1. ge에이=(엑스에이)+에이=(엑스+에이)+
  2. eq에이=(ge에이에이+1)+eq에이1엑스=에이0

엑스 가중치 1, 2, 4, ... 및 바이어스 0을 입니다. 비용 : (20 + 1) * 1 = 21.

ge2=(엑스2)+2=(엑스+2)+ 합니다. 비용 (1 + 1) * 2 = 4.

축적2=(ge22+1)+ge=(ge2(2))+=(ge2+(2))+

축적=(221축적2ge+1)+ge5=(ge(5))+5=(ge+(5))+

축적5=(221축적ge55+1)+ge7=(ge5(75))+7=(ge5+(75))+

...

축적1048571=(221축적1048559ge10485711048571+1)+ge1048573=(ge1048571(10485731048571))+1048573=(ge1048571+(10485731048571))+. 비용 (3 + 1) * 3 = 12.

레이어 82027 : 출력 축적1048573=(221축적1048571ge10485731048573+1)+. 비용 (3 + 1) * 1 = 4.

임계 값은 0입니다. 복식으로 작업하는 경우 오버플로 + 가능하지만 규칙에 완벽하게 부합하는 것 같습니다.

점수는 (82026-3) * 12 + 21 + 4 + 9 + 4입니다.


시원한. 내가 이해하는 것처럼, 이것은 또한 소수를 "기억"하지만 "병렬"보다는 "순차적으로"평등을 테스트합니다. (또는 기준선의 전치와 유사합니다.) 첫 번째 단계는 즉시 비트 패턴에서 벗어나 실제 정수 자체로 작업하는 것입니다. 결과적으로 동등성 검사에서 20 배의 패널티가 없습니다. 제출해 주셔서 감사합니다
A. Rex

위첨자 플러스 란 무엇입니까?
feersum

1
@feersum, 그것은 질문에 링크 된 ReLU의 Wikipedia 페이지의 표기법입니다. 엑스+=최대(0,엑스)
피터 테일러
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.