用RNN实现一个退位减法器

使用python编写一个简单的循环神经网络拟合一个退位减法的操作

1.定义基本函数

首先手动写一个sigmoid函数机器导数

import copy, numpy as np
np.random.seed(0) #固定随机生成器的种子,可以每次得到一样的值
def sigmoid(x): #激活函数
    output = 1/(1+np.exp(-x))
    return output

def sigmoid_output_to_derivative(output):#激活函数的倒数
    return output*(1-output)

2.建立二进制映射

定义的减法最大限制在256之内,即8位二进制的减法,定义int与二进制之间的映射数组int2binary。

int2binary = {}#整数到其二进制的映射
binary_dim = 8#暂时制作256以内的减法

#计算0~256的二进制表示
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]

3.定义参数

定义学习参数:隐藏层的权重synase_0、循环节点的权重synapse_h(输入节点16,输出节点16)、输出层的权重synapse_1(输入节点16,输出节点1)。为了减小复杂度,这里只设权重w,b被忽略。

#定义参数
alpha = 0.9#学习速率
input_dim = 2#输入的我的维度是2,减数和被减数
hidden_dim = 16
output_dim = 1#输出维度为1

#初始化网络
synapse_0 = (2*np.random.random((input_dim, hidden_dim))-1)*0.05#维度为2*16,2是输入维度,16是隐藏层维度
synapse_1 = (2*np.random.random((hidden_dim, output_dim))-1)*0.05
synapse_h = (2*np.random.random((hidden_dim, hidden_dim))-1)*0.05 #=>[-0.05, 0.05]

#用于存放反向传播的权重你更新值
synapse_0_update = np.zeros_like(synapse_0)
synapse_1_update = np.zeros_like(synapse_1)
synapse_h_update = np.zeros_like(synapse_h)

4.准备样本数据

(1)建立循环生成样本的数据,先生成两个数a和b。如果a小于b就变换位置,保证被减数大。

(2)计算出相间的结果c。

(3)将三个数字转换成二进制,为模型计算做准备。

将上面过程一一实现,代码如下:

#开始训练
for j in range(10000):
    #生成一个数字a
    a_int = np.random.randint(largest_number)
    #生成一个数字b,b的最大去只是largest_number/2,作为被减数,让它小一点
    b_int = np.random.randint(largest_number/2)
    #如果生成的大于b了,那么交换一下
    if a_int < b_int:
        tt = b_int
        b_int = a_int
        a_int = tt

    a = int2binary[a_int] #二进制编码
    b = int2binary[b_int] #二进制编码
    #正确的答案
    c_int = a_int - b_int
    c = int2binary[c_int]

5.模型初始化

初始化输出值为0,初始化总误差为0,定义layer_2_delats存储反向传播过程中的循环层的误差,layer_1_values为隐藏层的输出值,由于第一个数据传入时,没有前面的隐藏层输出值作为本次的输入,所以要为其定义一个初始值,这里定义为0.1。

    #存储神经网络的预测值
    d = np.zeros_like(c)
    overallError = 0 #每次把总误差清零

    layer_2_deltas = list() #存储每个时间点输出层的误差
    layer_1_values = list() #存储每个时间点隐藏层的值

    layer_1_values.append(np.ones(hidden_dim)*0.1) #一开始没有隐藏层,所以初始化一下原始值为0.1

6.正向传播

循环遍历每个二进制位,从各位开始一次相减,并将中间层隐藏层的输出传入下一位的计算,吧每一个时间点的误差导数都记录下来,同时统计总误差,为输出做准备。

    #正向传播
    for position in range(binary_dim):
        #生成输入和输出
        X = np.array([[a[binary_dim - position - 1], b[binary_dim - position - 1]]]) #从右到左,每次取两个输入数字的一个bit位
        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)) #隐藏层*隐藏层到输出层的转化矩阵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]) #记录每一个预测bit位

        #将隐藏层保存起来。下个时间序列便可以使用
        layer_1_values.append(copy.deepcopy(layer_1)) #记录下隐藏层的值,在下一个时间点用
    future_layer_1_delta = np.zeros(hidden_dim)

7.反向训练

初始化之后,开始从高位往回遍历,一次对每一位的所有层计算误差,并根据每层误差对权重求偏导,的到其调整值,最终将每一位算出的各层权重的调整值加在一起乘以学习率,来更新各层的权重,完成一次优化训练。

    #反向传播,从最后一个时间点到第一个时间点
    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

8.输出结果

每运行800次将结果输出,代码如下:

    #打印输出过程
    if(j%800 == 0):
        print("总误差:" +str(overallError))
        print("Pred:" +str(d))
        print("True:" +str(c))
        out = 0
        for index, x in enumerate(reversed(d)):
            out += x*pow(2, index)
        print(str(a_int) + "-" +str(b_int) + "=" + str(out))
        print("=====================================================================")

运行代码,输结果如下:

总误差:[3.99972855]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 1 1 0 0 1 1]
60-9=0
=====================================================================
总误差:[2.486562]
Pred:[0 0 0 0 0 0 0 0]
True:[0 0 0 1 0 0 0 1]
17-0=0
=====================================================================
总误差:[3.51869416]
Pred:[0 0 1 0 0 1 1 0]
True:[0 0 0 1 1 1 1 0]
89-59=38
=====================================================================
总误差:[0.18361106]
Pred:[0 0 0 1 1 0 0 0]
True:[0 0 0 1 1 0 0 0]
43-19=24
=====================================================================
总误差:[0.1709148]
Pred:[0 0 0 0 0 0 1 0]
True:[0 0 0 0 0 0 1 0]
73-71=2
=====================================================================
总误差:[0.13827615]
Pred:[0 0 1 1 1 1 0 0]
True:[0 0 1 1 1 1 0 0]
71-11=60
=====================================================================
总误差:[0.08982648]
Pred:[1 0 0 0 0 0 0 0]
True:[1 0 0 0 0 0 0 0]
230-102=128
=====================================================================
总误差:[0.17024705]
Pred:[0 1 1 1 0 0 0 1]
True:[0 1 1 1 0 0 0 1]
160-47=113
=====================================================================
总误差:[0.06442929]
Pred:[0 1 0 1 1 0 0 1]
True:[0 1 0 1 1 0 0 1]
92-3=89
=====================================================================
总误差:[0.04940924]
Pred:[0 0 0 1 1 0 1 1]
True:[0 0 0 1 1 0 1 1]
44-17=27
=====================================================================
总误差:[0.04009697]
Pred:[1 0 0 1 0 1 1 0]
True:[1 0 0 1 0 1 1 0]
167-17=150
=====================================================================
总误差:[0.06397785]
Pred:[1 0 0 1 1 0 0 0]
True:[1 0 0 1 1 0 0 0]
204-52=152
=====================================================================
总误差:[0.02595276]
Pred:[1 1 0 0 0 0 0 0]
True:[1 1 0 0 0 0 0 0]
209-17=192
=====================================================================

Process finished with exit code 0

可以看到,刚开始还不准,随着迭代次数的增加,到后来已经可以完全拟合退位减法了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值