您的位置:首页 > 其它

《Neural Networks and Deep Learning》codes' note

2017-07-27 08:48 459 查看
2017年7月27日

MIL day 18

Below is my NOTES for the first code file Network.py in 《Neural Networks and Deep Learning》wrote by Michael Nielsen

Maybe you can find all codes in this book here

Also, there are still some mistakes in this note, you can mail to me if you have any problems.

Besides, I will try to note more for other codes in the document.

"""First import the standard library "random" and third-library "numpy"
if we have the library which is made by ourself, we can import them then.
the order is: >>standard library  >>third-party library >>library by ourself"""
################以下为class Network定义#####################
# 应注意块代码的缩进问题
# 而且关于类名的命名 首字母最好大写 括号内为继承的父类 若不写则默认继承自object object属于父类最顶端
class Network(object):

"""__init__用于对bias和weight进行正态分布的初始化"""
def __init__(self,sizes):
#__xxx__表示这是一个特殊方法 方法中定义第一个为self用于对类进行实例化时自动传递实例
self.num_layers = len(sizes)
# len() 用于计算sizes的维度 传递给 实例属性self.num_layers
self.sizes = sizes
self.biases = [np.random.randn(y,1) for y in sizes[1:]]
# 以上np.random.randn(y,1)为进行随机初始化y*1的正态分布的array存入biases
# for y in sizes[1:] 为列表推导式
# sizes[1:]为切片操作
# 如:sizes[1:5:2]  表示截取索引为1(包含)至索引为5(不包含)步长为2的序列
# 如果第一个不写默认为0 第二个不写默认为-1 第三个不写默认步长为1 此时后一个:也可以省略
# 因为for y in sizes[1:] 会被执行不止一次 所以产生的bias为多维
# 因此整个np.random.randn外面要加[]
self.weights = [np.random.randn(y,x) for x,y in zip(sizes[:-1],sizes[1:])]
# 以上zip()函数用于接受多个序列参数 返回tuple list
# 如:    >>> x = [1,2,3]     >>> y = [4,5,6]     >>> z = [7,8,9]
# >>>xyz = zip(x,y,z)
# >>> print xyz         >>> [(1,4,7),(2,5,8),(3,6,9)]

"""feedforward前向传播 输入激活值  输出最后一层激活值"""
def feedforward(self,a):
# define feedforward method and the parameter is 'a'   注意冒号
for b,w in zip(self.biases,self.weights):
a = sigmoid(np.dot(w,a) + b)
return a
# 经过zip函数后 此时b w为每一层的biases 和 weights
# 假设输入为784个神经元 第一个隐藏层为30个神经元
# 则biases为30*1的array  weights为30*784的array 第一次a为784*1的array
# 因此np.dot(w,a)是array的矩阵乘法 经过for循环后 return 最后一层的激活值
# sigmoid函数为下方定义的函数 用来计算S型函数的值 详细函数定义在下

"""SGD 输入为training_data epochs mini_batch_size eta (test_data)

d2f7
整个method用于执行epochs次迭代期 每次迭代期打印数据
同时每一迭代期内 随机选取小批量样本数据 每一mini_batchs 计算梯度并更新bias and weights"""
def SGD(self,training_data,epochs,mini_batch_size,eta,test_data=None):
# define SGD method and default parameter 'test_data' is None,
# 迭代期epochs  小批量数据mini_batch_size 学习速率eta
if test_data: n_test = len(test_data)
n = len(training_data)
# 计算training_data的长度 同时如果test_data is not none 计算test_data 的长度
for j in xrange(epochs):
# 循环epochs迭代期的次数 以下为一个epoch做的工作
random.shuffle(training_data)
# random.shuffle(x) 随机将list中数据进行乱序打乱
mini_batches = [training_data[k:k+mini_batch_size] for k in xrange(0,n,mini_batch_size)]
# 对于已经打乱的training_data
# 从第0个数据开始
# 每隔mini_batch_size切出一块training_data 全部放入mini_batches list中
# 此时 mini_batches 可看作(n/mini_batch_size) 份 mini_batch_size training_data的array
# (实际上 training_data仍旧有维度)
for mini_batch in mini_batches:
self.update_mini_batch(mini_batch,eta)
# 对于mini_batches中每份mini_batch_size training_data
# 执行update_mini_batch() 更新实例属性self.biases和weights
# 下文method中
if test_data:
print "epoch {0}:{1}/{2}".format(j,self.evaluate(test_data),n_test)
else:
print "epoch {0} complete".format(j)
# string.format()函数 格式化字符串 通过{}代替%
# 如:>>>'{0},{1}'.format('name',2017)            则print 为 'name,2017'
# 如果有test_data print "epoch %d:%d/%d " %(j,evaluate(test_data),n_test)
# 否则 print 形如 "epoch 3 complete"

# range(1,6,2)为产生从1(包含)到6(不包含)步长为2的list
# 第一个数默认为0 第三个数默认为1 第二个数不可省略
# xrange()和range()类似 但是xrange不产生整个list 适用于大范围list 不会在内存创建list 只在循环中使用
# 如:xrange(5)  则实际产生[1,2,3,4] 但是不在内存创建

"""update_mini_batch method
输入mimi_batch eta  该方法执行一次后即完成一次mini_batch 然后直接更新实例属性"""
def update_mini_batch(self,mini_batch,eta):
nabla_b = [np.zeros(b.shape) for b in self.biases]
# 初始化一个和biases相同维度的全零array 用于存储梯度和
nabla_w = [np.zeros(w.shape) for w in self.weights]
# 同上
for x,y in mini_batch:
# 该for循环用于累加计算整个mini_batch上的梯度的和
delta_nabla_b, delta_nabla_w = self.backprop(x,y)
# 计算一个mini_batch的梯度
nabla_b = [nb + dnb for nb, dnb in zip(nabla_b,delta_nabla_b)]
# 迭代计算将此次mini_batch上的b的梯度加上nabla_b 再传递给nabla_b
# 在以上这一for循环中 执行一次是执行一层 最后形成形如biases的array 传递给nabla_b
nabla_w = [nw + dnw for nw, dnw in zip(nabla_w,delta_nabla_w)]  # 同上
self.weights = [w-(eta/len(mini_batch)*nw for w,nw in zip(self.weights,nabla_w)]
# 更新weights (每个mini_batch更新一次)
# w(this mini_batch) = w(last mini_batch) 减去 (eta*(梯度的和)/mini_batch_size)
self.biases = [b-(eta/len(mini_batch)*nb for b,nb in zip(self.biases,nabla_b)]
# 同上
# x.shape 返回array或者matrix x 的维度 以tuple的形式

"""backpropagation  应用feedforward计算每一层的带权输入和激活值 然后计算输出层的误差
再应用backpropagation计算every layers every neurons 的误差 求得关于b的梯度
关于w的梯度为误差*前一层对应激活值(转置)"""
def backprop(self,x,y):
nabla_b = [np.zeros(b.shape) for b in self.biases]
# 初始化一个和biases相同维度的全零array 用于存储b w的梯度
nabla_w = [np.zeros(w.shape) for w in self.weights]
# 同上
activation = x              # 将输入赋给activation 作为激活值
activations = [x]           # 作为array的元素传给activations list
zs = []                     # 初始化一个用来放置带权输入的zs list
for b,w in zip(self.biases,self.weights):
z = np.dot(w,activation) + b
# 计算带权输入z for表示每一层每一层的计算
zs.append(z)
# 将元素z(代表一层的z)增加到zs list中  zs.append(元素)
activation = sigmoid(z)
# 计算S型函数 即为该层的激活值
# 以上7行代码为前向传播   计算每一层的带权输入z和激活值
delta = self.cost_derivative(activations[-1],y) * sigmoid_prime(zs[-1])
# BP1  activations[-1]为输出层激活值 (二次代价函数的导数即为a-y)
# 所以以上公式为求输出层误差delta
nabla_b[-1] = delta
# BP3 神经元的误差即为其关于b的偏导数  所以将输出层上C关于b的梯度存入nabla_b[-1]
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
# BP4  当前层C关于w的梯度是当前层的误差*前一层的激活值
# x.transpose()为对array x 转置
# 若神经元为【4,3,2】 则delta为2*1 activations[-2]为3*1
# activations[-2].transpose()为1*3 此时矩阵相乘则得2*3 array 为输出层w梯度
for l in xrange(2,self.num_layers):
# 从倒数第二层到第二层  应用backpropagation
z = zs[-l]
sp = sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].transpose(),delta)*sp
# BP2 计算(-l)层神经元误差 l层w的转置*l层的误差*l层S型函数的导数
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta,activations[-l-1].transpose())
return (nabla_b,nabla_w)
# 返回整个神经网络每一层每一神经元的b和w的梯度

"""测试函数 输入test_data 输出为the target numbers in all test_data numbers"""
def evaluate(self,test_data):
# 计算test_data number中达到target的总数
test_results = [(np.argmax(self.feedforward(x)),y) for (x,y) in test_data]
# np.argmax()用于选取list中最大值,同时将该最大值和对应test_data的y作为一个tuple,然后放入list test_results
return sum(int(x == y) for (x,y) in test_results)
# 比对list中每个tuple的x与y 如果相等为1 否则为0 然后对整个求和  返回值即为target numbers in all numbers

"""function cost 的导数derivative  返回值为输入参数out_activations 和y的差  纯粹为了直观"""
def cost_derivative(self,output_activations,y):
return (output_activations - y)

################以下为函数定义#####################
def sigmoid(z):                                 # The sigmoid function.
return 1.0/(1.0+np.exp(-z))                 #np.exp(x) numpy内置函数  返回e的x次方  实际输入为array
""" 如果是手动输入一个3*1的list需要先显式将list转化为array  再进行np.exp(-x)运算 否则exp()无法进行list中元素取负号的运算 运行np.exp(x)则不会
如果随机化一个array 则不会出现任何问题且能计算该array中所有元素的exp()的值"""
def sigmoid_prime(z):                           # Derivative of the sigmoid function
return sigmoid(z)*(1-sigmoid(z))            # S型函数的导数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: