文章列表
1.自己编写深度学习框架ANNbox【高仿tensorflow】__01实现全连接.
2.自己编写深度学习框架ANNbox【高仿tensorflow】__02实现不同的优化方法.
3.自己编写深度学习框架ANNbox【高仿tensorflow】__03文本情感分析.
4.自己编写深度学习框架ANNbox【高仿tensorflow】__04卷积神经网络编写:实现AlexNet.
5.自己编写深度学习框架ANNbox【高仿tensorflow】__05循环神经网络编写:实现RNN(01).
…
自己编写神经网络库ANNbox【高仿tensorflow】__05循环神经网络编写:实现RNN(01)
为了实现RNN,真是费了老劲了,幸好工作氛围不错,业余还有精力来编ANNbox,在这里要感谢单位。我想尽量用之前定义好的函数来实现最基本的RNN。其实许多时候,在tensorflow中,我们可以不用调用诸如rnn_cell.BasicRNNCell、tf.nn.rnn这样的函数,而是通过自己定义rnn模块的前向计算函数,利用tensorflow强大的计算图结构管理就可以实现循环神经网络。而实现这些需要以下4点技术支持:
01. 拓扑结构管理
02. 域名控制(variable_scope)
03. 各类逻辑操作(concat/unstack)
04. 基本的数学运算(对各类tensor的加减乘除/激活函数/优化器/……)
通过前面几节工作的铺垫,到这里,第1点及第4点都已经基本实现,本节着重介绍第2点及第3点的原理实现,最后给出一个利用框架实现二进制加法运算的算例。
6.1 variable_scope类(利用上下文管理器实现变量的作用域限定)
变量名是在基类Node中定义的,所以用全局变量scope_name来传递作用域名称(默认值为’’),scope_name在variable_scope的初始化函数中被赋值,并在variable_scope上下文作用域范围结束的时候重新赋为空字符。用gloaba_resue(默认值为FALSE)来传递同名get_variable变量是否重用,在variable_scope上下文作用域范围结束的时候重新赋为False。相应的编程实现如下:
class variable_scope():
def __init__(self,local_scope_name='',reuse=False):
scope_name[0]=local_scope_name
global_resue[0]=reuse
def __enter__(self):
'''enter()返回一个对象。上下文管理器会使用这一对象作为as所指的变量'''
return self
def __exit__(self, exc_type, exc_val, exc_tb):
'''
结束上下文作用域的范围,并将global_resue设置为0
'''
scope_name[0]=''
global_resue[0]=False
为了实现name传递,我在Node基类中对变量名称加上了variable_scope域名对其命名的管理
class Node(object):
def __init__(self,inbound_nodes=[],name='',value=None,shape=[],is_get_variable=False):
......
if(scope_name[0]!='' and name!=''):
self.name=scope_name[0]+'/'+name
if(scope_name[0]!='' and name==''):
self.name=scope_name[0]
if(scope_name[0]=='' and name!=''):
self.name=name
......
6.2 get_variable类(创建共享变量)
get_variable和variable的区别网上有许多,这里不再相信介绍。但我需要声明一下循环神经网络中为什么要用到get_variable(),其实这里的get_variable主要是用来创建共享变量,由前面 深度学习基础模型算法原理及编程实现–06.循环神经网络(https://blog.csdn.net/drilistbox/article/details/79720859) 中不难发现,在RNN的每层神经网络中,沿时间轴及深度轴传递的权重矩阵都是一致的,即每个权重矩阵会用到许多次,但用的都是同一内存上的变量,且还需分别更新,因此需要用的get_variable创建共享变量:即在reuse=true及get_variable变量拥有同样的名字的情况下,不同的get_variable实例对象拥有不同地址,但是其属性self.value是共享的,共享的value值用全局共用字典get_variable_dict来管理。
在编程实现的时候,我们用一个全局变量get_variable_dict来存放get_variable实例对象的value值,每次创建get_variable实例的时候需判定当前对象在get_variable_dict中是否存在同名变量,并且当前variable_scope中的reuse是否为True,通过这两个因素来决定是重复调用get_variable_dict中存放的get_variable实例对象的值。
如果我们在计算图中某个节点定义了一个变量w,后来又在其他节点用get_variable来同一作用域下定义同名变量w/w,下面的3个图给出了大致流程:
图3中定义了一个同名的get_variable实例对象w,且当前作用域的resue为true,由于当前get_variable_dict的键值中有同名变量,所以共享在get_variable_dict已有的权重系数的内存空间。编程实现如下:
class get_variable(Node):
'''变量节点,功能仿造tensorflow中的get_variable,用于存放输入权重系数及偏执系数'''
def __init__(self,name='',shape=[],initializer=None):
'''输入层的节点没有传入节点(inbound nodes),所有无需向构造函数中传递任何inbound nodes'''
Node.__init__(self,name=name,value=None,is_get_variable=True)
self.shape=shape
if(self.name in get_variable_dict.keys() and global_resue[0]==True):
'''get_variable_dict中存在同名变量,并且可以重用,因此无需分配内存'''
self.value=get_variable_dict[self.name]
if(self.name in get_variable_dict.keys() and global_resue[0]==False):
'''get_variable_dict中存在同名变量,需要将resue设为True,否则为False'''
raise Exception(self,"already exist in current scrope, do you want to set reuse to True!")
# '''get_variable_dict中存在同名变量,并且不可以重用,因此需管理名称,并分配内存'''
# '''名称管理'''
# ind=0
# while self.name+str(ind) in get_variable_dict.keys():
# ind+=1
# self.name=self.name+str(ind)
# '''重新分配内存'''
# get_variable_dict[self.name]=initializer(shape)
# self.value=get_variable_dict[self.name]
# '''暂时封存'''
## trainables.append(self) #将variable添加进trainables列表中
if(self.name not in get_variable_dict.keys()):
'''get_variable_dict中不存在同名变量,则不管能不能重用,均需分配内存'''
get_variable_dict[self.name]=initializer(shape)
self.value=get_variable_dict[self.name]
'''暂时封存'''
# trainables.append(self) #将variable添加进trainables列表中
'''所有的get_variable变量都要加入trainables'''
trainables.append(self) #将variable添加进trainables列表中
if(isLEnd[0]==False):L_initial_with_variable_and_constant.append(self) #将variable存入节点列表L中
def forward(self):
pass
# self.value = self.initializer(self.shape)
def backward(self):
self.gradients={self:np.zeros_like(self.value)}
for n in self.outbound_nodes:
grad_cost=n.gradients[self]
self.gradients[self]+=grad_cost*1
6.3 concat类(多个张亮按指定维度合并为一个张量)
上图中详细的符号定义及代码推导请参考【深度学习基础模型算法原理及编程实现–03.全链接】。相应的代码实现如下:
class concat(Node):
'''
tf.concat(concat_dim, values, name='concat')
这里将多个tensor合并为一个tensor,所以当前类concat自身就可以用来做作为合并后的tensor,无需向unstack中用中间传输节点transmit_node
'''
def __init__(self, axis, *L, name=''):
Node.__init__(self, inbound_nodes=L[0], name=name)
self.axis=axis
#缺少检查所有inbound_nodes节点在 除了self.axis这一维度上的 shape是否相等,必须要有
self.shape=L[0][0].shape[:]
self.shape[self.axis]=0
for L_ in L[0]:
self.shape[self.axis]+=L_.shape[self.axis]
def forward(self):
len_shape=len(self.shape)
# print(self.shape)
self.value=np.zeros(self.shape)
ind_start=0
for L_ in self.inbound_nodes:
exec('self.value['+':,'*self.axis+str(ind_start)+':'+str(ind_start+L_.shape[self.axis])+',:'*(len_shape-1-self.axis)+']=L_.value')
ind_start+=L_.shape[self.axis]
def backward(self):
len_shape=len(self.shape)
self.gradients={n: np.zeros_like(n.value) for n in self.inbound_nodes}
grad_cost=self.outbound_nodes[0].gradients[self]
ind_start=0
for in_node in self.inbound_nodes:
exec('self.gradients[in_node]=grad_cost['+':,'*self.axis+str(ind_start)+':'+str(ind_start+in_node.shape[self.axis])+',:'*(len_shape-1-self.axis)+']')
ind_start+=in_node.shape[self.axis]
6.4 unstack类(一个R维张量按指定维度拆分为多个R-1维张量)
上图中详细的符号定义及代码推导请参考【[深度学习基础模型算法原理及编程实现–06.循环神经网络][6]】。相应的代码实现如下:
class unstack(Node):
'''
tf.concat(concat_dim, values, name='concat')
这里将多个tensor合并为一个tensor,所以当前类concat自身就可以用来做作为合并后的tensor,无需向unstack中用中间传输节点transmit_node
'''
def __init__(self, axis, *L, name=''):
Node.__init__(self, inbound_nodes=L[0], name=name)
self.axis=axis
#缺少检查所有inbound_nodes节点在 除了self.axis这一维度上的 shape是否相等,必须要有
self.shape=L[0][0].shape[:]
self.shape[self.axis]=0
for L_ in L[0]:
self.shape[self.axis]+=L_.shape[self.axis]
def forward(self):
len_shape=len(self.shape)
# print(self.shape)
self.value=np.zeros(self.shape)
ind_start=0
for L_ in self.inbound_nodes:
exec('self.value['+':,'*self.axis+str(ind_start)+':'+str(ind_start+L_.shape[self.axis])+',:'*(len_shape-1-self.axis)+']=L_.value')
ind_start+=L_.shape[self.axis]
def backward(self):
len_shape=len(self.shape)
self.gradients={n: np.zeros_like(n.value) for n in self.inbound_nodes}
grad_cost=self.outbound_nodes[0].gradients[self]
ind_start=0
for in_node in self.inbound_nodes:
exec('self.gradients[in_node]=grad_cost['+':,'*self.axis+str(ind_start)+':'+str(ind_start+in_node.shape[self.axis])+',:'*(len_shape-1-self.axis)+']')
ind_start+=in_node.shape[self.axis]
6.5 transmit_node类(用于unstack分解操作用的中间暂存变量,不放入feed_dict_inside【非常重要】)
6.6 验证算例
详细的代码如下:
6.6.1 ANNbox版本:
# -*- coding: utf-8 -*-
import sys
sys.path.append('./../ANNbox')
import ANNbox as tf
#import tensorflow as tf
import numpy as np
fp=open('annbox_febug.txt',"w")
def binary2int(binary):
out=0
for index,num in enumerate(binary[::-1]):
out += num*pow(2,index)
return out
def binary2int2(binary):
out=0
for index,num in enumerate(binary):
out += num*pow(2,index)
return out
'''RNN参数设置,计算8位二进制加法,因此序列长度为8,每次向place_holder中喂入batch_size大小的数据,包括两个相加的二进制序列和一个结果二进制序列,hidden_size表示RNN的隐层节点维数'''
sequence_length=8
batch_size=100
input_size=2
hidden_size=8
output_size= 1
epochs=500
seed_=1
n_classes=output_size
state_size=hidden_size
##RNN的初始化状态,全设为零。注意state是与input保持一致,接下来会有concat操作,所以这里要有batch的维度。即每个样本都要有隐层状态
#init_state=tf.zeros([batch_size,state_size],dtype=tf.float32)
#定义rnn_cell的权重参数,
with tf.variable_scope('rnn_cell'):
W=tf.get_variable('W',[input_size+state_size,state_size],initializer=tf.truncated_normal_initializer(mean=0.,stddev=0.1))
b=tf.get_variable('b',[state_size],initializer=tf.constant_initializer(0.0))
#使之定义为reuse模式,循环使用,保持参数相同
rnn_cell_ind=0
def rnn_cell(rnn_input,state):
with tf.variable_scope('rnn_cell',reuse=True):
W=tf.get_variable('W',[input_size+state_size,state_size],initializer=tf.truncated_normal_initializer(mean=0.,stddev=0.1))
b=tf.get_variable('b',[state_size],initializer=tf.constant_initializer(0.0))
# 定义rnn_cell具体的操作,这里使用的是最简单的rnn,不是LSTM
global rnn_cell_ind
rnn_cell_ind+=1
cnt=tf.concat(1,(rnn_input,state),name='cnt'+str(rnn_cell_ind)+'_in_rnn_cell')
mtl=tf.matmul(cnt,W,name='mtl'+str(rnn_cell_ind)+'_in_rnn_cell')
add=tf.Add(mtl,b,name='add'+str(rnn_cell_ind)+'_in_rnn_cell')
return tf.nn.tanh(add,name='tanh'+str(rnn_cell_ind))
# return tf.tanh(tf.matmul(tf.concat((rnn_input,state),1),W)+b)
'''推断过程,即是RNN模型的构建过程'''
def inference(x_input):
'''将输入转换到tensorflow中RNN需要的格式'''
rnn_inputs=tf.unstack(input_holder,axis=1,name='x_inputs')
'''构建rnn单元和rnn网络'''
'''方法01,简单两句搞定,关键在与返回值上,输出看了下,states只有两个值,应该一个是初始状态,一个是末状态,而其他的中间状态其实涵盖在outputs里了,需要直接去取就好,不知道LSTM的输出是怎样,states会不会是8个,得去试一下'''
#rnn_cell=tf.nn.rnn_cell.BasicRNNCell(hidden_size)
#rnn_outputs,final_state=tf.nn.rnn(rnn_cell,rnn_inputs,dtype=tf.float32)
'''方法02'''
#RNN的初始化状态,全设为零。注意state是与input保持一致,接下来会有concat操作,所以这里要有batch的维度。即每个样本都要有隐层状态
init_state=tf.zeros([batch_size,state_size],name='init_state')
state=init_state
rnn_outputs=[]
#循环num_steps次,即将一个序列输入RNN模型
for rnn_input in rnn_inputs:
state=rnn_cell(rnn_input,state)
rnn_outputs.append(state)
'''定义输出层权值和偏置,用variable_scope和get_variable是为了变量的不重复,否则测试和训练会构建重复的variable出来,Tensorboard里面网络的graph会变的比较乱'''
with tf.variable_scope('hidden_to_output_layer'):
w2out=tf.get_variable('w2out',shape=[hidden_size,output_size],initializer=tf.truncated_normal_initializer(mean=0.,stddev=0.1))
bias2out=tf.get_variable('bias2out',shape=[output_size],initializer=tf.constant_initializer(0.0))
logits=[tf.Add(tf.matmul(rnn_output,w2out,name='matmul_'+str(ind)),bias2out,name='Add'+str(ind)) for ind,rnn_output in enumerate(rnn_outputs)]
y_pred=tf.concat(1,[tf.nn.sigmoid(logit,name='sigmoid_'+str(ind)) for ind,logit in enumerate(logits)],name='concat_result')
return y_pred
def generateBinaryDict():
largest_number=pow(2,8)
int2binary={}
binary=np.unpackbits(np.array([range(largest_number)],dtype=np.uint8).T,axis=1).astype('float32')
for i in range(largest_number):
int2binary[i]=binary[i]
return int2binary
input_holder=tf.placeholder(tf.float32,[batch_size,8,2],name='input_placeholder')
label_holder=tf.placeholder(tf.float32,[batch_size,8],name='label_placeholder')
y_pred=inference(input_holder)
sub=tf.nn.Sub(y_pred,label_holder,name='sub')
loss=tf.reduce_mean(tf.square(sub,name='square'),name='reduce_mena')
#op=tf.train.AdagradOptimizer(1.0).minimize(loss)
learning_rate=1e0
Momentum_rate=0.95
op=tf.train.MomentumOptimizer(learning_rate,Momentum_rate).minimize(loss)
init=tf.global_variables_initializer()
np.random.seed(seed_)
#with tf.Session() as sess:
init.run()
binary_dict=generateBinaryDict()
for e in range(epochs):
batch_in_data=[]
batch_out_data=[]
reshape_bi_list=[]
'''生成minibatch data,其实没必要,但是为了规范'''
for i in range(batch_size):
x_int1=np.random.randint(0,128)
x_int2=np.random.randint(0,128)
y_true_int=x_int1+x_int2
x_bi1=binary_dict[x_int1]
x_bi2=binary_dict[x_int2]
y_true_bi=binary_dict[y_true_int]
'''注意输入和输出都需要反向,因为建的RNN是从前往后传信息的,而二进制加法的进位规则是从后往前计算并进位的'''
for j in range(8):
reshape_bi_list.append(x_bi1[7-j])
reshape_bi_list.append(x_bi2[7-j])
batch_out_data.append(y_true_bi[::-1])
batch_in_data=np.reshape(reshape_bi_list,[-1,8,2])
batch_out_data=np.reshape(batch_out_data,[-1,8])
op.run(feed_dict={input_holder: batch_in_data,label_holder: batch_out_data})
loss_value=loss.my_eval(feed_dict={input_holder: batch_in_data,label_holder: batch_out_data})
y_pred_value=y_pred.my_eval(feed_dict={input_holder: batch_in_data,label_holder: batch_out_data})
# print('iteration at: % d,mean loss is: %f' % (e,loss_value))
x_left=np.array([binary2int2(l) for l in batch_in_data[:,:,0]])
x_right=np.array([binary2int2(l) for l in batch_in_data[:,:,1]])
pre=np.array([binary2int2(l) for l in np.round(y_pred_value)])
# print('acc:',(((x_left+x_right)==pre).astype('int')).mean())
print('iteration at: % d,mean loss is: %f acc:%f' % (e,loss_value,(((x_left+x_right)==pre).astype('int')).mean()))
tem=W.value
fp.write('epoch:{}, W:\n'.format(e))
for ind in range(tem.shape[0]):
for jnd in range(tem.shape[1]):
fp.write('{:3.3f} '.format(tem[ind,jnd]))
fp.write('\n')
tem=b.value
fp.write('epoch:{}, W:\n'.format(e))
for ind in range(len(tem)):
fp.write('{:3.3f} '.format(tem[ind]))
fp.write('\n')
fp.write('iteration at:{:3.2f},mean loss is:{:3.2f}, acc:{:3.2f}'.format(e,loss_value,(((x_left+x_right)==pre).astype('int')).mean()))
fp.close()
6.6.2 tensorflow版本:
# -*- coding:utf-8 -*-
#import sys
#sys.path.append('./../ANNbox')
#import ANNbox as tf
import tensorflow as tf
import numpy as np
fp=open('tensorflow_febug.txt',"w")
def binary2int(binary):
out=0
for index,num in enumerate(binary[::-1]):
out += num*pow(2,index)
return out
def binary2int2(binary):
out=0
for index,num in enumerate(binary):
out += num*pow(2,index)
return out
'''RNN参数设置,计算8位二进制加法,因此序列长度为8,每次向place_holder中喂入batch_size大小的数据,包括两个相加的二进制序列和一个结果二进制序列,hidden_size表示RNN的隐层节点维数'''
sequence_length=8
batch_size=100
input_size=2
hidden_size=8
output_size= 1
epochs=500
seed_=1
n_classes=output_size
state_size=hidden_size
##RNN的初始化状态,全设为零。注意state是与input保持一致,接下来会有concat操作,所以这里要有batch的维度。即每个样本都要有隐层状态
#init_state=tf.zeros([batch_size,state_size],dtype=tf.float32)
#定义rnn_cell的权重参数,
with tf.variable_scope('rnn_cell'):
W=tf.get_variable('W',[input_size +state_size,state_size],initializer=tf.truncated_normal_initializer(mean=0.,stddev=0.1,seed=seed_))
b=tf.get_variable('b',[state_size],initializer=tf.constant_initializer(0.0))
#使之定义为reuse模式,循环使用,保持参数相同
rnn_cell_ind=0
def rnn_cell(rnn_input,state):
with tf.variable_scope('rnn_cell',reuse=True):
W=tf.get_variable('W',[input_size+state_size,state_size],initializer=tf.truncated_normal_initializer(mean=0.,stddev=0.1,seed=seed_))
b=tf.get_variable('b',[state_size],initializer=tf.constant_initializer(0.0))
# 定义rnn_cell具体的操作,这里使用的是最简单的rnn,不是LSTM
global rnn_cell_ind
rnn_cell_ind+=1
cnt=tf.concat(1,(rnn_input,state))
mtl=tf.matmul(cnt,W)+b
return tf.nn.tanh(mtl)
# return tf.tanh(tf.matmul(tf.concat((rnn_input,state),1),W)+b)
'''推断过程,即是RNN模型的构建过程'''
def inference(x_input):
'''将输入转换到tensorflow中RNN需要的格式'''
rnn_inputs=tf.unstack(x_input,axis=1)
'''构建rnn单元和rnn网络'''
'''方法01,简单两句搞定,关键在与返回值上,输出看了下,states只有两个值,应该一个是初始状态,一个是末状态,而其他的中间状态其实涵盖在outputs里了,需要直接去取就好,不知道LSTM的输出是怎样,states会不会是8个,得去试一下'''
# rnn_cell=tf.nn.rnn_cell.BasicRNNCell(hidden_size)
# rnn_outputs,final_state=tf.nn.rnn(rnn_cell,rnn_inputs,dtype=tf.float32)
'''方法02'''
#RNN的初始化状态,全设为零。注意state是与input保持一致,接下来会有concat操作,所以这里要有batch的维度。即每个样本都要有隐层状态
init_state=tf.zeros([batch_size,state_size])
state=init_state
rnn_outputs=[]
#循环num_steps次,即将一个序列输入RNN模型
for rnn_input in rnn_inputs:
state=rnn_cell(rnn_input,state)
rnn_outputs.append(state)
'''定义输出层权值和偏置,用variable_scope和get_variable是为了变量的不重复,否则测试和训练会构建重复的variable出来,Tensorboard里面网络的graph会变的比较乱'''
with tf.variable_scope('hidden_to_output_layer'):
w2out=tf.get_variable('w2out',shape=[hidden_size,output_size],initializer=tf.truncated_normal_initializer(mean=0.,stddev=0.1,seed=seed_))
bias2out=tf.get_variable('bias2oput',shape=[output_size],initializer=tf.constant_initializer(0.0))
logits=[tf.matmul(rnn_output,w2out)+bias2out for ind,rnn_output in enumerate(rnn_outputs)]
y_pred=tf.concat(1,[tf.nn.sigmoid(logit) for ind,logit in enumerate(logits)])
return y_pred
def generateBinaryDict():
largest_number=pow(2,8)
int2binary={}
binary=np.unpackbits(np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
for i in range(largest_number):
int2binary[i]=binary[i]
return int2binary
input_holder=tf.placeholder(tf.float32,[None,8,2])
label_holder=tf.placeholder(tf.float32,[None,8])
y_pred=inference(input_holder)
loss=tf.reduce_mean(tf.square(y_pred-label_holder))
#op=tf.train.AdagradOptimizer(1.0).minimize(loss)
learning_rate=1e0
Momentum_rate=0.95
op=tf.train.MomentumOptimizer(learning_rate,Momentum_rate).minimize(loss)
init=tf.initialize_all_variables()
np.random.seed(seed_)
with tf.Session() as sess:
sess.run(init)
binary_dict=generateBinaryDict()
for e in range(epochs):
batch_in_data=[]
batch_out_data=[]
reshape_bi_list=[]
'''生成minibatch data,其实没必要,但是为了规范'''
for i in range(batch_size):
x_int1=np.random.randint(0,128)
x_int2=np.random.randint(0,128)
y_true_int=x_int1+x_int2
x_bi1=binary_dict[x_int1]
x_bi2=binary_dict[x_int2]
y_true_bi=binary_dict[y_true_int]
'''注意输入和输出都需要反向,因为建的RNN是从前往后传信息的,而二进制加法的进位规则是从后往前计算并进位的'''
for j in range(8):
reshape_bi_list.append(x_bi1[7-j])
reshape_bi_list.append(x_bi2[7-j])
batch_out_data.append(y_true_bi[::-1])
batch_in_data=np.reshape(reshape_bi_list,[-1,8,2])
batch_out_data=np.reshape(batch_out_data,[-1,8])
sess.run(op,feed_dict={input_holder:batch_in_data,label_holder:batch_out_data})
loss_value=sess.run(loss,feed_dict={input_holder:batch_in_data,label_holder:batch_out_data})
y_pred_value=sess.run(y_pred,feed_dict={input_holder:batch_in_data,label_holder:batch_out_data})
x_left=np.array([binary2int2(l) for l in batch_in_data[:,:,0]])
x_right=np.array([binary2int2(l) for l in batch_in_data[:,:,1]])
pre=np.array([binary2int2(l) for l in np.round(y_pred_value)])
print('iteration at:% d,mean loss is:%f acc:%f' % (e,loss_value,(((x_left+x_right)==pre).astype('int')).mean()))
tem=sess.run(W)
fp.write('epoch:{}, W:\n'.format(e))
for ind in range(tem.shape[0]):
for jnd in range(tem.shape[1]):
fp.write('{:3.3f} '.format(tem[ind,jnd]))
fp.write('\n')
tem=sess.run(b)
fp.write('epoch:{}, W:\n'.format(e))
for ind in range(len(tem)):
fp.write('{:3.3f} '.format(tem[ind]))
fp.write('\n')
fp.write('iteration at:{:3.2f},mean loss is:{:3.2f}, acc:{:3.2f}'.format(e,loss_value,(((x_left+x_right)==pre).astype('int')).mean()))
fp.close()
6.7 代码文件
https://pan.baidu.com/s/1QIE0a6NdJOEKO7_pKtNTOQ#list/path=%2F