吴恩达机器学习练习:神经网络(反向传播)

时间:2022-09-12 19:10:37

1 Neural Networks 神经网络

1.1 Visualizing the data 可视化数据

这部分我们随机选取100个样本并可视化。训练集共有5000个训练样本,每个样本是20*20像素的数字的灰度图像。每个像素代表一个浮点数,表示该位置的灰度强度。2020的像素网格被展开成一个400维的向量。在我们的数据矩阵X中,每一个样本都变成了一行,这给了我们一个5000400矩阵X,每一行都是一个手写数字图像的训练样本。


	
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from scipy.io import loadmat
  4. import scipy.optimize as opt
  5. from sklearn.metrics import classification_report # 这个包是评价报告

	
  1. def load_mat(path):
  2. '''读取数据'''
  3. data = loadmat('ex4data1.mat') # return a dict
  4. X = data['X']
  5. y = data['y'].flatten()
  6. return X, y

	
  1. def plot_100_images(X):
  2. """随机画100个数字"""
  3. index = np.random.choice(range(5000), 100)
  4. images = X[index]
  5. fig, ax_array = plt.subplots(10, 10, sharey=True, sharex=True, figsize=(8, 8))
  6. for r in range(10):
  7. for c in range(10):
  8. ax_array[r, c].matshow(images[r*10 + c].reshape(20,20), cmap='gray_r')
  9. plt.xticks([])
  10. plt.yticks([])
  11. plt.show()

	
  1. X,y = load_mat('ex4data1.mat')
  2. plot_100_images(X)

吴恩达机器学习练习:神经网络(反向传播)

1.2 Model representation 模型表示

我们的网络有三层,输入层,隐藏层,输出层。我们的输入是数字图像的像素值,因为每个数字的图像大小为20*20,所以我们输入层有400个单元(这里不包括总是输出要加一个偏置单元)。

吴恩达机器学习练习:神经网络(反向传播)

1.2.1 load train data set 读取数据

首先我们要将标签值(1,2,3,4,…,10)转化成非线性相关的向量,向量对应位置(y[i-1])上的值等于1,例如y[0]=6转化为y[0]=[0,0,0,0,0,1,0,0,0,0]。


	
  1. from sklearn.preprocessing import OneHotEncoder
  2. def expand_y(y):
  3. result = []
  4. # 把y中每个类别转化为一个向量,对应的lable值在向量对应位置上置为1
  5. for i in y:
  6. y_array = np.zeros(10)
  7. y_array[i-1] = 1
  8. result.append(y_array)
  9. '''
  10. # 或者用sklearn中OneHotEncoder函数
  11. encoder = OneHotEncoder(sparse=False) # return a array instead of matrix
  12. y_onehot = encoder.fit_transform(y.reshape(-1,1))
  13. return y_onehot
  14. '''
  15. return np.array(result)

获取训练数据集,以及对训练集做相应的处理,得到我们的input X,lables y。


	
  1. raw_X, raw_y = load_mat('ex4data1.mat')
  2. X = np.insert(raw_X, 0, 1, axis=1)
  3. y = expand_y(raw_y)
  4. X.shape, y.shape
  5. '''
  6. ((5000, 401), (5000, 10))
  7. '''
  8. .csdn.net/Cowry5/article/details/80399350

1.2.2 load weight 读取权重

这里我们提供了已经训练好的参数1,2,存储在ex4weight.mat文件中。这些参数的维度由神经网络的大小决定,第二层有25个单元,输出层有10个单元(对应10个数字类)。


	
  1. def load_weight(path):
  2. data = loadmat(path)
  3. return data['Theta1'], data['Theta2']

	
  1. t1, t2 = load_weight('ex4weights.mat')
  2. t1.shape, t2.shape
  3. # ((25, 401), (10, 26))

1.2.3 展开参数

当我们使用高级优化方法来优化神经网络时,我们需要将多个参数矩阵展开,才能传入优化函数,然后再恢复形状。


	
  1. def serialize(a, b):
  2. '''展开参数'''
  3. return np.r_[a.flatten(),b.flatten()]

	
  1. theta = serialize(t1, t2) # 扁平化参数,25*401+10*26=10285
  2. theta.shape # (10285,)

	
  1. def deserialize(seq):
  2. '''提取参数'''
  3. return seq[:25*401].reshape(25, 401), seq[25*401:].reshape(10, 26)

1.3 Feedforward and cost function 前馈和代价函数 1.3.1 Feedforward

确保每层的单元数,注意输出时加一个偏置单元,s(1)=400+1,s(2)=25+1,s(3)=10。

吴恩达机器学习练习:神经网络(反向传播)


	
  1. def sigmoid(z):
  2. return 1 / (1 + np.exp(-z))

	
  1. def feed_forward(theta, X,):
  2. '''得到每层的输入和输出'''
  3. t1, t2 = deserialize(theta)
  4. # 前面已经插入过偏置单元,这里就不用插入了
  5. a1 = X
  6. z2 = a1 @ t1.T
  7. a2 = np.insert(sigmoid(z2), 0, 1, axis=1)
  8. z3 = a2 @ t2.T
  9. a3 = sigmoid(z3)
  10. return a1, z2, a2, z3, a3

	
  1. a1, z2, a2, z3, h = feed_forward(theta, X)

1.3.2 Cost function

回顾下神经网络的代价函数(不带正则化项)

吴恩达机器学习练习:神经网络(反向传播)

输出层输出的是对样本的预测,包含5000个数据,每个数据对应了一个包含10个元素的向量,代表了结果有10类。在公式中,每个元素与log项对应相乘。

最后我们使用提供训练好的参数,算出的cost应该为0.287629


	
  1. def cost(theta, X, y):
  2. a1, z2, a2, z3, h = feed_forward(theta, X)
  3. J = 0
  4. for i in range(len(X)):
  5. first = - y[i] * np.log(h[i])
  6. second = (1 - y[i]) * np.log(1 - h[i])
  7. J = J + np.sum(first - second)
  8. J = J / len(X)
  9. return J
  10. '''
  11. # or just use verctorization
  12. J = - y * np.log(h) - (1 - y) * np.log(1 - h)
  13. return J.sum() / len(X)
  14. '''

	
  1. cost(theta, X, y) # 0.2876291651613189

1.4 Regularized cost function 正则化代价函数

吴恩达机器学习练习:神经网络(反向传播)

注意不要将每层的偏置项正则化。

最后You should see that the cost is about 0.383770


	
  1. def regularized_cost(theta, X, y, l=1):
  2. '''正则化时忽略每层的偏置项,也就是参数矩阵的第一列'''
  3. t1, t2 = deserialize(theta)
  4. reg = np.sum(t1[:,1:] ** 2) + np.sum(t2[:,1:] ** 2) # or use np.power(a, 2)
  5. return l / (2 * len(X)) * reg + cost(theta, X, y)

	
  1. regularized_cost(theta, X, y, 1) # 0.38376985909092354

2 Backpropagation 反向传播 

2.1 Sigmoid gradient S函数导数

吴恩达机器学习练习:神经网络(反向传播)

这里可以手动推导,并不难。


	
  1. def sigmoid_gradient(z):
  2. return sigmoid(z) * (1 - sigmoid(z))

2.2 Random initialization 随机初始化

当我们训练神经网络时,随机初始化参数是很重要的,可以打破数据的对称性。一个有效的策略是在均匀分布(−e,e)中随机选择值,我们可以选择 e = 0.12 这个范围的值来确保参数足够小,使得训练更有效率。


	
  1. def random_init(size):
  2. '''从服从的均匀分布的范围中随机返回size大小的值'''
  3. return np.random.uniform(-0.12, 0.12, size)

2.3 Backpropagation 反向传播

吴恩达机器学习练习:神经网络(反向传播)

目标:获取整个网络代价函数的梯度。以便在优化算法中求解。

这里面一定要理解正向传播和反向传播的过程,才能弄清楚各种参数在网络中的维度,切记。比如手写出每次传播的式子。


	
  1. print('a1', a1.shape,'t1', t1.shape)
  2. print('z2', z2.shape)
  3. print('a2', a2.shape, 't2', t2.shape)
  4. print('z3', z3.shape)
  5. print('a3', h.shape)
  6. ''www.cppcns.com'
  7. a1 (5000, 401) t1 (25, 401)
  8. z2 (5000, 25)
  9. a2 (5000, 26) t2 (10, 26)
  10. z3 (5000, 10)
  11. a3 (5000, 10)
  12. '''

	
  1. def gradient(theta, X, y):
  2. '''
  3. unregularized gradient, notice no d1 since the input layer has no error
  4. return 所有参数theta的梯度,故梯度D(i)和参数theta(i)同shape,重要。
  5. '''
  6. t1, t2 = deserialize(theta)
  7. a1, z2, a2, z3, h = feed_forward(theta, X)
  8. d3 = h - y # (5000, 10)
  9. d2 = d3 @ t2[:,1:] * sigmoid_gradient(z2) # (5000, 25)
  10. D2 = d3.T @ a2 # (10, 26)
  11. D1 = d2.T @ a1 # (25, 401)
  12. D = (1 / len(X)) * serialize(D1, D2) # (10285,)
  13. return D

2.4 Gradient checking 梯度检测

在你的神经网络,你是最小化代价函数J()。执行梯度检查你的参数LwzbXHHo,你可以想象展开参数(1)(2)成一个长向量。通过这样做,你能使用以下梯度检查过程。

吴恩达机器学习练习:神经网络(反向传播)


	
  1. def gradient_checking(theta, X, y, e):
  2. def a_numeric_grad(plus, minus):
  3. """
  4. 对每个参数theta_i计算数值梯度,即理论梯度。
  5. """
  6. return (regularized_cost(plus, X, y) - regularized_cost(minus, X, y)) / (e * 2)
  7. numeric_grad = []
  8. for i in range(len(theta)):
  9. plus = theta.copy() # deep copy otherwise you will change the raw theta
  10. minus = theta.copy()
  11. plus[i] = plus[i] + e
  12. minus[i] = minus[i] - e
  13. grad_i = a_numeric_grad(plus, minus)
  14. numeric_grad.append(grad_i)
  15. numeric_grad = np.array(numeric_grad)
  16. analytic_grad = regularized_gradient(theta, X, y)
  17. diff = np.linalg.norm(numeric_grad - analytic_grad) / np.linalg.norm(numeric_grad + analytic_grad)
  18. print('If your backpropagation implementation is correct,\nthe relative difference will be smaller than 10e-9 (assume epsilon=0.http://www.cppcns.com0001).\nRelative Difference: {}\n'.format(diff))

	
  1. gradient_checking(theta, X, y, epsilon= 0.0001)#这个运行很慢,谨慎运行

2.5 Regularized Neural Networks 正则化神经网络

吴恩达机器学习练习:神经网络(反向传播)


	
  1. def regularized_gradient(theta, X, y, l=1):
  2. """不惩罚偏置单元的参数"""
  3. a1, z2, a2, z3, h = feed_forward(theta, X)
  4. D1, D2 = deserialize(gradient(theta, X, y))
  5. t1[:,0] = 0
  6. t2[:,0] = 0
  7. reg_D1 = D1 + (l / len(X)) * t1
  8. reg_D2 = D2 + (l / len(X)) * t2
  9. return serialize(reg_D1, reg_D2)

2.6 Learning parameters using fmincg 优化参数


	
  1. def nn_training(X, y):
  2. init_theta = random_init(10285) # 25*401 + 10*26
  3. res = opt.minimize(fun=regularized_cost,
  4. x0=init_theta,
  5. args=(X, y, 1),
  6. method='TNC',
  7. jac=regularized_gradient,
  8. options={'maxiter': 400})
  9. return res

	
  1. res = nn_training(X, y)#慢
  2. res
  3. '''
  4. fun: 0.5156784004838036
  5. jac: array([-2.51032294e-04, -2.11248326e-12, 4.38829369e-13, ...,
  6. 9.88299811e-05, -2.59923586e-03, -8.52351187e-04])
  7. message: 'Converged (|f_n-f_(n-1)| ~= 0)'
  8. nfev: 271
  9. nit: 17
  10. status: 1
  11. success: True
  12. x: array([ 0.58440213, -0.02013683, 0.1118854 , ..., -2.8959637 ,
  13. 1.85893941, -2.78756836])
  14. '''

	
  1. def accuracy(theta, X, y):
  2. _, _, _, _, h = feed_forward(res.x, X)
  3. y_pred = np.argmax(h, axis=1) + 1
  4. print(classification_report(y, y_pred))

	
  1. accuracy(res.x, X, raw_y)
  2. '''
  3. precision recall f1-score support
  4. 1 0.97 0.99 0.98 500
  5. 2 0.98 0.97 0.98 500
  6. 3 0.98 0.95 0.96 500
  7. 4 0.98 0.97 0.97 500
  8. 5 0.97 0.98 0.97 500
  9. 6 0.99 0.98 0.98 500
  10. 7 0.99 0.97 0.98 500
  11. 8 0.96 0.98 0.97 500
  12. 9 0.97 0.98 0.97 500
  13. 10 0.99 0.99 0.99 500
  14. avg / total 0.98 0.98 0.98 5000
  15. '''

3 Visualizing the hidden layer 可视化隐藏层

理解神经网络是如何学习的一个很好的办法是,可视化隐藏层单元所捕获的内容。通俗的说,给定一个的隐藏层单元,可视化它所计算的内容的方法是找到一个输入x,x可以激活这个单元(也就是说有一个激活值接近与1)。对于我们所训练的网络,注意到1中每一行都是一个401维的向量,代表每个隐藏层单元的参数。如果我们忽略偏置项,我们就能得到400维的向量,这个向量代表每个样本输入到每个隐层单元的像素的权重。因此可视化的一个方法是,reshape这个400维的向量为(20,20)的图像然后输出。

注:

It turns out that this is equivalent to finding the input that gives the highest activation for the hidden unit, given a norm constraint on the input.

这相当于找到了一个输入,给了隐层单元最高的激活值,给定了一个输入的标准限制。例如(||x||2​≤1)

(这部分暂时不太理解)


	
  1. def plot_hidden(theta):
  2. t1, _ = deserialize(theta)
  3. t1 = t1[:, 1:]
  4. fig,ax_array = plt.subplots(5, 5, sharex=True, sharey=True, figsize=(6,6))
  5. for r in range(5):
  6. for c in range(5):
  7. ax_array[r, c].matshow(t1[r * 5 + c].reshape(20, 20), cmap='gray_r')
  8. plt.xticks([])
  9. plt.yticks([])
  10. plt.show()

	
  1. plot_hidden(res.x)

吴恩达机器学习练习:神经网络(反向传播)

到此在这篇练习中,你将学习如何用反向传播算法来学习神经网络的参数,更多相关机器学习,神经网络内容请搜索我们以前的文章或继续浏览下面的相关文章,希望大家以后多多支持我们!

原文链接:https://blog.csdn.net/Cowry5/article/details/80399350