第十九节,使用RNN实现一个退位减法器
退位减法具有RNN的特性,即输入的两个数相减时,一旦发生退位运算,需要将中间状态保存起来,当高位的数传入时将退位标志一并传入参与计算。
我们在做减法运算时候,把减数和被减数转换为二进制然后进行运算。我们定义一个RNN网络,输入节点数为2个,依次传入减数和被减数的二进制序列值,隐藏层节点数为16个,由于二进制减法输出值为0或者1,所以输出节点数为可以设置为1个或者2个,如果设置为2个,表示看成一个二分类问题;如果设置为1个,就表示输出的值,这里我们设置输出节点数为1个,输出层使用的是S型函数。
一 定义基本函数
由于使用二进制运算,每一位的值只可能为0或者1,因此激活函数全部选择S型函数。
#规定随机数生成器的种子,可以每次得到一样的值
np.random.seed(0)
'''
一 定义基本函数
''' def sigmoid(x):
'''
定义S型函数 args:
x:输入数或list、ndarray
'''
return 1/(1+np.exp(-x)) def sigmoid_output_to_derivative(output):
'''
定义sigmod的导数 args:
output:sigmoid函数的输出 假设要计算x=5时,sigmoid函数的导数,此处就传入sigmoid(5)
'''
return output*(1-output)
二 建立二进制映射关系
我们定义减法的最大限制在256以内,即8位数的减法,定义int与二进制之间的映射字典int2binary。
'''
二 建立二进制映射 定义的减法最大值限制在256以内,即8位二进制的减法,定义int与二进制之间的映射字典int2binary
'''
#整数到其二进制表示的映射字典
int2binary = {}
#二进制的位数
binary_dim = 8
#计算0-255的二进制表示
largest_number = pow(2,binary_dim)
'''
注意 np.array([range(largest_number)],dtype=np.uint8) 返回的是[[0,1,2,3...255]] 形状1x256 如果使用这个后面需要.T进行转置
np.array(range(largest_number),dtype=np.uint8) 返回的是[0,1,2,...255]形状为(256,) 尽量不使用这种形状不明确的
然后按行转为二进制 得到256x8
'''
binary = np.unpackbits(
np.array(range(largest_number),dtype=np.uint8).reshape(256,1),axis=1)
#建立int-二进制映射
for i in range(largest_number):
#向字典中追加数据
int2binary[i] = binary[i]
三 定义参数
定义权重和偏置,这里我们为了简化运算,忽略偏置。
'''
三 定义参数 隐藏层的权重synapse_0(2x16),输出层的权重synapse_1(16x1),循环节点的权重synapse_h(16x16)
这里只设置权重 忽略偏置
''' #参数设置
learning_rate = 0.9 #学习速率
input_dim = 2 #输入节点的个数为2,减数和被减数
hidden_dim = 16 #隐藏层节点个数
output_dim = 1 #输出节点个数 n_samples = 10000 #样本个数 #初始化网络 np.random.random生成一个[0,1)之间随机浮点数或size大小浮点数组
synapse_0 = (2*np.random.random((input_dim,hidden_dim))-1)*0.05 #-0.05~0.05之间
synapse_1 = (2*np.random.random((hidden_dim,output_dim))-1)*0.05 #-0.05~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)
四 准备样本数据
随机生成减数和被减数数据,并计算结果。这里要求被减数要大于减数。最后并把这些数转换为二进制。
'''
四 准备样本数据
''' #建立循环生成样本数据,先生成两个数a,b,如果a小于b,就交换位置,保证被减数大
for i in range(n_samples):
#生成一个数字a 被减数 范围[0,256)之间的整数
a_int = np.random.randint(largest_number)
#生成一个数字b 减数,b的最大值取得是largest_number/2
b_int = np.random.randint(largest_number/2)
#如果生成的b>a交换
if a_int < b_int:
tmp = b_int
b_int = a_int
a_int = tmp #二进制编码
a = int2binary[a_int] #被减数
b = int2binary[b_int] #减数
c = int2binary[a_int - b_int] #差值
五 模型初始化
初始化神经网络的预测值为0,初始化总误差为0,定义layer_2_deltas存储反向传播过程中输出层的误差,layer_1_values存放隐藏层的输出值,由于第一个数据传入时,没有前面的隐藏层输出值来作为本次的输入,所以需要为其定义一个初始值,这里定义为0.1.
'''
五 模型初始化
'''
d = np.zeros_like(c) #存储神经网络的预测值 初始化为0
over_all_error = 0 #初始化总误差为0 layer_2_deltas = list() #存储每个时间点输出层的误差
layer_1_values = list() #存储每个时间点隐藏层的值 layer_1_values.append(np.ones(hidden_dim)*0.1) #一开始没有隐藏层(t=1),所以初始化原始值为0.1
六 正向传播
循环遍历每个二进制,从个位开始依次相减,并将中间隐藏层的输出传入下一位的计算(退位减法),把每一个时间点的误差导数都记录下来,同时统计总误差,为输出准备。
'''
六 正向传播
'''
#循环遍历每一个二进制位
for position in range(binary_dim):
#生成输入和输出 从右向左,每次取两个输入数字的一个bit位
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_1_values.append(copy.deepcopy(layer_1)) #计算输出层
layer_2 = sigmoid(np.dot(layer_1,synapse_1)) #预测误差
layer_2_error = layer_2 - y
#把每个时间点的误差导数都记录下来
layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2)) #总误差
over_all_error += np.abs(layer_2_error[0]) #记录每个预测的bit位
d[binary_dim - position - 1] = np.round(layer_2[0][0]) fuature_layer_1_delta = np.zeros(hidden_dim)
最后一行代码是为了反向传播准备的初始化。同正向传播一样,反向传播是从最后一次往前反向计算误差,对于每一个当前的计算都需要有它的下一次结果参与。反向计算从最后一次开始的,它没有后一次的输入,这里初始化为0.
七 反向训练
初始化之后,开始从高位往回训练,一次对每一位的所有层计算误差,并根据每层误差对权重求梯度,得到其调整值,最终将每一位算出的各层权重的调整值加载一块乘以学习率,来更新各层的权重,完成一次优化训练。
'''
七 反向训练
'''
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 = (fuature_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) fuature_layer_1_delta = layer_1_delta #完成所有反向传播之后,更新权重矩阵,并把矩阵梯度变量清0
synapse_0 -= synapse_0_update*learning_rate
synapse_1 -= synapse_1_update*learning_rate
synapse_h -= synapse_h_update*learning_rate
synapse_0_update = 0
synapse_1_update = 0
synapse_h_update = 0
八 输出
'''
八 打印输出结果
'''
if i%800 == 0:
print('总误差:',str(over_all_error))
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('-------------------------------------------------')

可以看到随着迭代次数的增加,计算越来越准确。
完整代码:
# -*- coding: utf-8 -*-
"""
Created on Sun May 6 17:33:24 2018 @author: lenovo
""" '''
裸写一个退位减法器
使用python编写简单循环神经网络拟合一个退位减法的操作,观察其反向传播过程
''' import numpy as np
import copy #规定随机数生成器的种子,可以每次得到一样的值
np.random.seed(0)
'''
一 定义基本函数
''' def sigmoid(x):
'''
定义S型函数 args:
x:输入数或list、ndarray
'''
return 1/(1+np.exp(-x)) def sigmoid_output_to_derivative(output):
'''
定义sigmod函数输出的导数 args:
output: output:sigmoid函数的输出 假设要计算x=5时,sigmoid函数的导数,此处就传入sigmoid(5)
'''
return output*(1-output) '''
二 建立二进制映射 定义的减法最大值限制在256以内,即8位二进制的减法,定义int与二进制之间的映射字典int2binary
'''
#整数到其二进制表示的映射字典
int2binary = {}
#二进制的位数
binary_dim = 8
#计算0-255的二进制表示
largest_number = pow(2,binary_dim)
'''
注意 np.array([range(largest_number)],dtype=np.uint8) 返回的是[[0,1,2,3...255]] 形状1x256 如果使用这个后面需要.T进行转置
np.array(range(largest_number),dtype=np.uint8) 返回的是[0,1,2,...255]形状为(256,) 尽量不使用这种形状不明确的
然后按行转为二进制 得到256x8
'''
binary = np.unpackbits(
np.array(range(largest_number),dtype=np.uint8).reshape(256,1),axis=1)
#建立int-二进制映射
for i in range(largest_number):
#向字典中追加数据
int2binary[i] = binary[i] '''
三 定义参数 隐藏层的权重synapse_0(2x16),输出层的权重synapse_1(16x1),循环节点的权重synapse_h(16x16)
这里只设置权重 忽略偏置
''' #参数设置
learning_rate = 0.9 #学习速率
input_dim = 2 #输入节点的个数为2,减数和被减数
hidden_dim = 16 #隐藏层节点个数
output_dim = 1 #输出节点个数 n_samples = 10000 #样本个数 #初始化网络 np.random.random生成一个[0,1)之间随机浮点数或size大小浮点数组
synapse_0 = (2*np.random.random((input_dim,hidden_dim))-1)*0.05 #-0.05~0.05之间
synapse_1 = (2*np.random.random((hidden_dim,output_dim))-1)*0.05 #-0.05~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) '''
四 准备样本数据
''' #建立循环生成样本数据,先生成两个数a,b,如果a小于b,就交换位置,保证被减数大
for i in range(n_samples):
#生成一个数字a 被减数 范围[0,256)之间的整数
a_int = np.random.randint(largest_number)
#生成一个数字b 减数,b的最大值取得是largest_number/2
b_int = np.random.randint(largest_number/2)
#如果生成的b>a交换
if a_int < b_int:
tmp = b_int
b_int = a_int
a_int = tmp #二进制编码
a = int2binary[a_int] #被减数
b = int2binary[b_int] #减数
c = int2binary[a_int - b_int] #差值 '''
五 模型初始化
'''
d = np.zeros_like(c) #存储神经网络的预测值 初始化为0
over_all_error = 0 #初始化总误差为0 layer_2_deltas = list() #存储每个时间点输出层的误差
layer_1_values = list() #存储每个时间点隐藏层的值 layer_1_values.append(np.ones(hidden_dim)*0.1) #一开始没有隐藏层(t=1),所以初始化原始值为0.1 '''
六 正向传播
'''
#循环遍历每一个二进制位
for position in range(binary_dim):
#生成输入和输出 从右向左,每次取两个输入数字的一个bit位
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_1_values.append(copy.deepcopy(layer_1)) #计算输出层
layer_2 = sigmoid(np.dot(layer_1,synapse_1)) #预测误差
layer_2_error = layer_2 - y
#把每个时间点的误差导数都记录下来
layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2)) #总误差
over_all_error += np.abs(layer_2_error[0]) #记录每个预测的bit位
d[binary_dim - position - 1] = np.round(layer_2[0][0]) fuature_layer_1_delta = np.zeros(hidden_dim) '''
七 反向训练
'''
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 = (fuature_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) fuature_layer_1_delta = layer_1_delta #完成所有反向传播之后,更新权重矩阵,并把矩阵梯度变量清0
synapse_0 -= synapse_0_update*learning_rate
synapse_1 -= synapse_1_update*learning_rate
synapse_h -= synapse_h_update*learning_rate
synapse_0_update = 0
synapse_1_update = 0
synapse_h_update = 0 '''
八 打印输出结果
'''
if i%800 == 0:
print('总误差:',str(over_all_error))
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('-------------------------------------------------')
参考文献
第十九节,使用RNN实现一个退位减法器的更多相关文章
- 第三百八十九节,Django+Xadmin打造上线标准的在线教育平台—列表筛选结合分页
第三百八十九节,Django+Xadmin打造上线标准的在线教育平台—列表筛选结合分页 根据用户的筛选条件来结合分页 实现原理就是,当用户点击一个筛选条件时,通过get请求方式传参将筛选的id或者值, ...
- 第三百六十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索功能
第三百六十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索功能 Django实现搜索功能 1.在Django配置搜索结果页的路由映 ...
- 第三百五十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)介绍以及安装
第三百五十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)介绍以及安装 elasticsearch(搜索引擎)介绍 ElasticSearch是一个基于 ...
- 第三百四十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—cookie禁用、自动限速、自定义spider的settings,对抗反爬机制
第三百四十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—cookie禁用.自动限速.自定义spider的settings,对抗反爬机制 cookie禁用 就是在Scrapy的配置文件set ...
- 第三百三十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—Scrapy启动文件的配置—xpath表达式
第三百三十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—Scrapy启动文件的配置—xpath表达式 我们自定义一个main.py来作为启动文件 main.py #!/usr/bin/en ...
- 第三百二十九节,web爬虫讲解2—urllib库爬虫—ip代理—用户代理和ip代理结合应用
第三百二十九节,web爬虫讲解2—urllib库爬虫—ip代理 使用IP代理 ProxyHandler()格式化IP,第一个参数,请求目标可能是http或者https,对应设置build_opener ...
- centos MySQL主从配置 ntsysv chkconfig setup命令 配置MySQL 主从 子shell MySQL备份 kill命令 pid文件 discuz!论坛数据库读写分离 双主搭建 mysql.history 第二十九节课
centos MySQL主从配置 ntsysv chkconfig setup命令 配置MySQL 主从 子shell MySQL备份 kill命令 pid文件 discuz!论坛数 ...
- centos LAMP第一部分-环境搭建 Linux软件删除方式,mysql安装,apache,PHP,apache和php结合,phpinfo页面,ldd命令 第十九节课
centos LAMP第一部分-环境搭建 Linux软件删除方式,mysql安装,apache,PHP,apache和php结合,phpinfo页面,ldd命令 第十九节课 打命令之后可以输入: e ...
- 第二百四十九节,Bootstrap附加导航插件
第二百四十九节,Bootstrap附加导航插件 学习要点: 1.附加导航插件 本节课我们主要学习一下 Bootstrap 中的附加导航插件 一.附加导航 注意:此插件要使用 bootstrap3.0. ...
随机推荐
- Maven 项目 查找指定包的引用位置
- QTP 自动化测试--定义变量
1 Dim suffixsuffix=get_currentdatetxt("001")
- Maven依赖范围及传递
.Maven因为执行一系列编译.测试和部署运行等操作,在不同的操作下使用的classpath不同,依赖范围就是用来控制依赖与三种 classpath(编译classpath.测试classpath.运 ...
- 学习 Spring (九) 注解之 @Required, @Autowired, @Qualifier
Spring入门篇 学习笔记 @Required @Required 注解适用于 bean 属性的 setter 方法 这个注解仅仅表示,受影响的 bean 属性必须在配置时被填充,通过在 bean ...
- AgilePoint数据库模式中当前所有表的列表
表名 描述 WF_ACTIVITY_INSTS 包含有关活动实例的信息. WF_ASSIGNED_OBJECTS 包含有关用户角色的分配对象的信息. WF_AUDIT_TRAILS 包含有关流程模板的 ...
- vue的 v-for 循环中图片加载路径问题
先看一下产品需求,如下图所示, 产品要求图片和它的名称一一对应,本来是非常简单的需求,后台直接返回图片路径和名称,前台直接读取就可以了,但是我们没有存储图片的服务器,再加上是一个实验性的需求,图片需要 ...
- henrylee2cn简单的rpc服务
server 端package controllers import ( "github.com/henrylee2cn/teleport" "model/system& ...
- Spring MVC 使用介绍(一)—— 概述
一.Web MVC简介 1.经典的MVC架构 存在的问题:1.控制器负责流程控制.请求数据整理与校验.模型与视图选择等功能,过于复杂.2.模型层没有进行分层设计 2.改进的MVC设计 1)控制器功能拆 ...
- BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- bzoj 3626 : [LNOI2014]LCA (树链剖分+线段树)
Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1.设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先.有q ...