当前位置 博文首页 > 使用Python建立RNN实现二进制加法的示例代码

    使用Python建立RNN实现二进制加法的示例代码

    作者:及时行樂_ 时间:2021-07-16 18:49

    只有一百行左右代码,应该还是比较好理解的。

    首先看一下结果,

    The end error is:[0.05344101]

    发现还是不错的。如果不想看讲解,就直接跳到文末,有所有的代码,安装numpy库就能够跑。

    二进制加法

    这个没啥好说的,就是逢二进一,不知道的就看看计算机组成原理的相关内容吧。

    RNN主要学两件事,一个是前一位的进位,一个是当前位的加法操作。只告诉当前阶段和前一阶段的计算结果,让网络自己学习加法和进位操作。

    具体代码

    既然是神经网络,肯定就非线性的,首先是sigmoid函数,这个要是不清楚,就看看相关博客了解一下。

    反向传播的时候需要sigmoid函数的导数值,所以把两个函数就直接贴在下面了。

    # 前向传播
    def sigmoid(in_x):
      output = 1 / (1 + np.exp(-in_x))
      return output
    # 反向传播
    def sigmoid_output_to_derivative(output):
      return output * (1 - output)
    

    定义一个字典,因为待会儿要进行十进制和二进制之间的转换,我们用字典进行存储他们之间的对应关系。

    (在这里我们只选用八位二进制)

    int2binary = {}
    binary_dim = 8
    
    largest_number = pow(2, binary_dim)
    binary = np.unpackbits(
      np.array([range(largest_number)], dtype=np.uint8).T, axis=1)
    for i in range(largest_number):
      int2binary[i] = binary[i]
    
    

    再接着就是对我们的RNN进行初始化操作。

    alpha = 0.1
    input_dim = 2
    hidden_dim = 16
    output_dim = 1
    

    接着是生成神经网络各层的权重值以及反向传播时对权值矩阵进行更新的存储。

    # 生成神经网络各层的权重值(在0,1之间)
    synapse_0 = 2 * np.random.random((input_dim, hidden_dim)) - 1
    synapse_1 = 2 * np.random.random((hidden_dim, output_dim)) - 1
    synapse_h = 2 * np.random.random((hidden_dim, hidden_dim)) - 1
    # 反向传播对权重值的矩阵进行更新
    synapse_0_update = np.zeros_like(synapse_0)
    synapse_1_update = np.zeros_like(synapse_1)
    synapse_h_update = np.zeros_like(synapse_h)

    RNN

    我们进行10万次的迭代训练。

    我们进行的是加法,所以需要将值找到。最大取八位,所以a, b, c都不能超过,因为a+b=c,所以a, b不能超过最大的一半。其中largest_number表示8位二进制数对应的最大的十进制数值。

    # 最大取八位,所以a, b, c都不能超过,因为a+b=c,所以a, b不能超过最大的一半
    a_int = np.random.randint(largest_number / 2)
    a = int2binary[a_int]
    
    b_int = np.random.randint(largest_number / 2) 
    b = int2binary[b_int] 
    
    c_int = a_int + b_int
    c = int2binary[c_int]
    
    

    我们定义一个d来存储我们的预测值与实际值c进行比较,判断网络的能力。并且定义一个overallError来存储error值,并将初值设为0。

     d = np.zeros_like(c)
    
     overallError = 0
    

    最后我们在进行反向传播的时候,会计算一个loss值,在训练网络的过程中,我们需要计算w1,w2分别对这个loss值的影响。

    layer_2_deltas = list()
    layer_1_values = list()
    # 因为第一次迭代会用到l1的值,所以我们需要将列表用0来填充
    layer_1_values.append(np.zeros(hidden_dim))
    future_layer_1_delta = np.zeros(hidden_dim)
    

    我们需要先进行前向传播,再进行反向传播。

    在进行前向传播的过程中,我们需要将两个二进制传入,从最后一位开始,一层一层地通过sigmoid函数,得到预测值。然后通过预测值与准确值之间的差值设为l2层的loss值。有了这个loss值,我们就可以算出刚刚定义的layer_2_deltas(l2 层的权重参数)

    for position in range(binary_dim):
      X = np.array([[a[binary_dim - position - 1], b[binary_dim - position - 1]]])
      y = np.array([[c[binary_dim - position - 1]]]).T
    
      layer_1 = sigmoid(np.dot(X, synapse_0) + np.dot(layer_1_values[-1], synapse_h))
    
      layer_2 = sigmoid(np.dot(layer_1, synapse_1))
    
      layer_2_error = y - layer_2
      layer_2_deltas.append((layer_2_error) * sigmoid_output_to_derivative(layer_2))
      overallError += np.abs(layer_2_error[0])
    
      d[binary_dim - position - 1] = np.round(layer_2[0][0])
    
      layer_1_values.append(copy.deepcopy(layer_1))
    
    

    然后进行反向传播,也就是从最高位往后走。(具体的解释放在代码的注释中了)

    for position in range(binary_dim):
      X = np.array([[a[position], b[position]]])
      # 从参数列表中反向依次取值
      layer_1 = layer_1_values[-position - 1]
      # 因为要进行反向传播,所以还需要取到l1层的前一位的value
      prev_layer_1 = layer_1_values[-position - 2]
    
     # l2也是如此,delta列表中反向依次取值
      layer_2_delta = layer_2_deltas[-position - 1]
      # 通过公式进行计算l1的delta值
      layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)
    
     # 然后分别对w0, w1和wh进行更新
      synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
      synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
      synapse_0_update += X.T.dot(layer_1_delta)
    
      future_layer_1_delta = layer_1_delta
    

    然后再前向传播和反向传播结束之后,引入α \alphaα值进行参数的更新,并将updata重新置为0,以方便下一次循环使用。

    synapse_0 += synapse_0_update * alpha
    synapse_1 += synapse_1_update * alpha
    synapse_h += synapse_h_update * alpha
    
    synapse_0_update *= 0
    synapse_1_update *= 0
    synapse_h_update *= 0

    最后就是打印训练结果了,因为训练次数过多,所以这边设计每训练1万次打印一次结果。

      if j % 10000 == 0:
        print(str(j) + "/100000 :The error is:" + str(overallError))

    运行结果
    0/100000 :The error is:[3.45638663]
    10000/100000 :The error is:[0.3231264]
    20000/100000 :The error is:[0.27153112]
    30000/100000 :The error is:[0.1603061]
    40000/100000 :The error is:[0.10004929]
    50000/100000 :The error is:[0.11245508]
    60000/100000 :The error is:[0.11951541]
    70000/100000 :The error is:[0.07859761]
    80000/100000 :The error is:[0.06742156]
    90000/100000 :The error is:[0.08218885]
    The end error is:[0.05344101]

    最终代码

    import copy
    import numpy as np
    
    np.random.seed(0)
    
    def sigmoid(in_x):
      output = 1 / (1 + np.exp(-in_x))
      return output
    
    def sigmoid_output_to_derivative(output):
      return output * (1 - output)
    
    int2binary = {}
    binary_dim = 8
    
    largest_number = pow(2, binary_dim)
    binary = np.unpackbits(
      np.array([range(largest_number)], dtype=np.uint8).T, axis=1)
    for i in range(largest_number):
      int2binary[i] = binary[i]
    
    alpha = 0.1
    input_dim = 2
    hidden_dim = 16
    output_dim = 1
    
    # 生成神经网络各层的权重值(在0,1之间)
    synapse_0 = 2 * np.random.random((input_dim, hidden_dim)) - 1
    synapse_1 = 2 * np.random.random((hidden_dim, output_dim)) - 1
    synapse_h = 2 * np.random.random((hidden_dim, hidden_dim)) - 1
    # 反向传播对权重值的矩阵进行更新
    synapse_0_update = np.zeros_like(synapse_0)
    synapse_1_update = np.zeros_like(synapse_1)
    synapse_h_update = np.zeros_like(synapse_h)
    
    for j in range(100000):
      # 最大取八位,所以a, b, c都不能超过,因为a+b=c,所以a, b不能超过最大的一半
      a_int = np.random.randint(largest_number / 2) 
      a = int2binary[a_int] 
    
      b_int = np.random.randint(largest_number / 2) 
      b = int2binary[b_int] 
    
      c_int = a_int + b_int
      c = int2binary[c_int]
    
      d = np.zeros_like(c)
    
      overallError = 0
    
      layer_2_deltas = list()
      layer_1_values = list()
     # 因为第一次迭代会用到l1的值,所以我们需要将列表用0来填充
      layer_1_values.append(np.zeros(hidden_dim))
      future_layer_1_delta = np.zeros(hidden_dim)
    
      for position in range(binary_dim):
        X = np.array([[a[binary_dim - position - 1], b[binary_dim - position - 1]]])
        y = np.array([[c[binary_dim - position - 1]]]).T
    
        layer_1 = sigmoid(np.dot(X, synapse_0) + np.dot(layer_1_values[-1], synapse_h))
    
        layer_2 = sigmoid(np.dot(layer_1, synapse_1))
    
        layer_2_error = y - layer_2
        layer_2_deltas.append((layer_2_error) * sigmoid_output_to_derivative(layer_2))
        overallError += np.abs(layer_2_error[0])
    
        d[binary_dim - position - 1] = np.round(layer_2[0][0])
    
     layer_1_values.append(copy.deepcopy(layer_1))
    
      for position in range(binary_dim):
        X = np.array([[a[position], b[position]]])
        layer_1 = layer_1_values[-position - 1]
        prev_layer_1 = layer_1_values[-position - 2]
    
        layer_2_delta = layer_2_deltas[-position - 1]
        
        layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(
          synapse_1.T)) * sigmoid_output_to_derivative(layer_1)
    
        synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
        synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
        synapse_0_update += X.T.dot(layer_1_delta)
    
        future_layer_1_delta = layer_1_delta
    
      synapse_0 += synapse_0_update * alpha
      synapse_1 += synapse_1_update * alpha
      synapse_h += synapse_h_update * alpha
    
      synapse_0_update *= 0
      synapse_1_update *= 0
      synapse_h_update *= 0
    
      if j % 10000 == 0:
        print(str(j) + "/100000 :The error is:" + str(overallError))
    
    print("The end error is:" + str(overallError))
    
    
    jsjbwy
    上一篇:没有了
    下一篇:没有了