【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络和深度学习


上一篇:​​【课程1 - 第二周作业】​​ ✌✌✌✌ ​​【目录】​​ ✌✌✌✌ 下一篇:​​【课程1 - 第三周作业】​​


首先说明一下,大学生一枚,最近在学习神经网络,写这篇文章只是记录自己的学习历程,起总结复习的作用,别无它意,本文参考了​​zsffuture的博客​​、​​布衣先生real的博客​​、​​孔小爽的博客​​、​​何宽的博客​​以及​​Github上fengdu78老师的文章​​进行学习


✌ 我们要实现一个能够分类样本点的神经网络

  • numpy:常用数学工具库
  • matplotlib:python的画图工具
  • LogisticRegression:逻辑回归模型
  • lightgbm:lgb模型
  • cross_val_score:交叉验证
import numpy as np
import matplotlib.pyplot as plt
from testCases import *
import sklearn.datasets
from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
from sklearn.linear_model import LogisticRegression
import lightgbm as lgb
from sklearn.model_selection import cross_val_score

✌ 加载训练测试数据集

  • X:特征矩阵
  • Y:标签
X, Y = load_planar_dataset()

✌ 打印数据集的详细数据

print('样本数量:',X.shape[1])
print('特征数量:',X.shape[0])
print('X的维度:',X.shape)
print('Y的维度:',Y.shape)

查看输出结果:

样本数量: 400
特征数量: 2
X的维度: (2, 400)
Y的维度: (1, 400)

✌ 神经网络介绍

现在我们的准备工作已经做好了,接下来就是搭建神经网络
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_神经网络_02
单一样本的损失:
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_03
计算所有样本的平均损失值:
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_04
搭建神经网络的主要步骤是:

  1. 定义模型结构(例如输入特征的数量)
  2. 初始化模型的参数
  3. 不断迭代(调整参数):
    3.1 计算当前损失(正向传播)
    3.2 计算当前梯度(反向传播)
    3.3 更新参数(梯度下降)

✌ 定义sigmoid函数

【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_神经网络_05


【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_深度学习_06

【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_深度学习_07

因为我们要做的是二分类问题,所以到最后要将其转化为概率,所以可以利用sigmoid函数的性质将其转化为0~1之间

def sigmoid(z):
"""
功能:激活函数,计算sigmoid的值

参数:
z:任何维度的矩阵

返回:
s:sigmoid(z)
"""
s=1/(1+np.exp(-z))

return s

✌ 定义各网络层的节点数

这里我们为什么要获取各个层的节点数呢?
原因是在初始化w、b等参数时需要确定其维度,以便于后面的传播计算

def layer_size(X,Y):
"""
功能:获得各个网络层的节点数

参数:
X:特征矩阵
Y:标签

返回:
in_layer:输出层的节点数
hidden_layer:隐藏层的节点数
out_layer:输出层的节点数
"""
# 本数据集为两个特征
in_layer=X.shape[0]
# 自己定义隐藏层为4个节点
hidden_layer=4
# 输出层为1维
out_layer=Y.shape[0]

return in_layer,hidden_layer,out_layer

✌ 定义初始化w、b的函数

在进行梯度下降之前,要初始化w和b的值,但是这里会有个问题,为了方便我们会把w、b的值全部初始化为0,这样做是不正确的,原因是:如果都为0,会导致在传播计算时,模型对称,各个节点的参数不起作用,可以自己推到一下
所以我们要给w、b进行随机取值
本文参数维度:

  • W1:(4,2)
  • b1:(4,1)
  • W2:(1,4)
  • b2:(1,1)
def init_w_b(in_layer,hidden_layer,out_layer):
"""
功能:初始化w,b的维度和值

参数:
in_layer:输出层的节点数
hidden_layer:隐藏层的节点数
out_layer:输出层的节点数

返回:
init_params:对应各层参数的字典
"""
# 定义随机种子,以便之后每次产生随机数唯一
np.random.seed(2021)
# 初始化参数,符合高斯分布
W1=np.random.randn(hidden_layer,in_layer)*0.01
b1=np.random.randn(hidden_layer,1)
W2=np.random.randn(out_layer,hidden_layer)*0.01
b2=np.random.randn(out_layer,1)

init_params={'W1':W1,
'b1':b1,
'W2':W2,
'b2':b2}

return init_params

✌ 定义向前传播函数

神经网络分为正向传播和反向传播
正向传播计算求出损失函数,然后反向计算各个梯度
然后进行梯度下降,更新参数
计算公式:
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_神经网络_08
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_09
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_机器学习_10
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_机器学习_11

def forward(W1,b1,W2,b2,X,Y):
"""
功能:向前传播,计算出各个层的激活值和未激活值(A1,A2,Z1,Z2)

参数:
W1:隐藏层的权值参数
b1:隐藏层的偏置参数
W2:输出层的权值参数
b2:输出层的偏置参数
X:特征矩阵
Y:标签

返回:
forward_params:对应各层数据的字典
"""
# 计算隐藏层
Z1=np.dot(W1,X)+b1
# 激活
A1=np.tanh(Z1)
# 计算第二层
Z2=np.dot(W2,A1)+b2
# 激活
A2=sigmoid(Z2)

forward_params={'Z1':Z1,
'A1':A1,
'Z2':Z2,
'A2':A2,
'W1':W1,
'b1':b1,
'W2':W2,
'b2':b2}

return forward_params

✌ 定义损失函数

损失函数为交叉熵,数值越小,表明模型越优秀
计算公式:
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_机器学习_12

def loss_fn(W1,b1,W2,b2,X,Y):
"""
功能:构造损失函数,计算损失值

参数:
W1:隐藏层的权值参数
b1:隐藏层的偏置参数
W2:输出层的权值参数
b2:输出层的偏置参数
X:特征矩阵
Y:标签

返回:
loss:模型损失值
"""
# 样本数
m=X.shape[1]
# 向前传播,获取激活值A2
forward_params=forward(W1,b1,W2,b2,X,Y)

A2=forward_params['A2']
# 计算损失值
loss=np.multiply(Y,np.log(A2))+np.multiply(1-Y,np.log(1-A2))
loss=-1/m*np.sum(loss)
# 降维,如果不写,可能会有错误
loss=np.squeeze(loss)

return loss

✌ 定义向后传播函数

反向计算各个梯度
然后进行梯度下降,更新参数
计算公式:
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_深度学习_13
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_14
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_15
第一层的参数同理,这里要记住计算各参数梯度,就是高数中的链式法则,这也就是为什么叫做向后传播,想要计算前一层的参数导数值就要先计算出后一层的梯度值
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_深度学习_16
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_python_17
【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_python_18
记住这个一切都OK!!!

def backward(W1,b1,W2,b2,X,Y):
"""
功能:向后传播,计算各个层参数的梯度(偏导)

参数:
W1:隐藏层的权值参数
b1:隐藏层的偏置参数
W2:输出层的权值参数
b2:输出层的偏置参数
X:特征矩阵
Y:标签

返回:
grads:各参数的梯度值
"""
# 样本数
m=X.shape[1]
# 进行前向传播,获取各参数值
forward_params=forward(W1,b1,W2,b2,X,Y)

A1=forward_params['A1']
A2=forward_params['A2']
W1=forward_params['W1']
W2=forward_params['W2']
# 计算梯度
dZ2= A2 - Y
dW2 = (1 / m) * np.dot(dZ2, A1.T)
db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True)
dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2))
dW1 = (1 / m) * np.dot(dZ1, X.T)
db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True)

grads = {"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2 }

return grads

✌ 定义整个传播过程

一个传播流程包括:

  1. 向前传播
  2. 计算损失函数
  3. 向后传播
    3.1 计算梯度
    3.2 更新参数
def propagate(W1,b1,W2,b2,X,Y):
"""
功能:传播运算,向前->损失->向后->

参数:
W1:隐藏层的权值参数
b1:隐藏层的偏置参数
W2:输出层的权值参数
b2:输出层的偏置参数
X:特征矩阵
Y:标签

返回:
grads,loss:各参数的梯度,损失值
"""
# 计算损失
loss=loss_fn(W1,b1,W2,b2,X,Y)
# 向后传播,计算梯度
grads=backward(W1,b1,W2,b2,X,Y)

return grads,loss

✌ 定义优化器函数

目标是通过最小化损失函数 J来学习 w 和 b 。对于参数 λ,更新规则是 【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_19 【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_神经网络_20【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_深度学习_21【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_人工智能_22,其中 λ 是学习率。
num_iter代表梯度下降时的迭代次数,就是w的改变次数,求取损失函数的最小值,即全局最优解,这里可能会产生局部最优解,会影响模型结果,这里不与阐述,可以选择其他较好的优化器
大多数优化器都是基于梯度下降这种方法,只不过具体的数学计算有些不同

def optimizer(W1,b1,W2,b2,X,Y,num_iter,lr,print_loss=False):
"""
功能:优化函数,进行梯度下降,不断更新权重值,求取最优解

参数:
W1:隐藏层的权值参数
b1:隐藏层的偏置参数
W2:输出层的权值参数
b2:输出层的偏置参数
X:特征矩阵
Y:标签
num_iter:梯度下降时的迭代次数
lr:学习率 w=w-lr*dw
print_loss:是否每100次迭代打印一次缺失值

返回:
params:训练好后的w和b值
losses:各迭代次数下的损失值
"""
# 存储不同迭代次数下的loss值
losses=[]
# 开始迭代
for i in range(num_iter):
# 获取损失和梯度值
grads,loss=propagate(W1,b1,W2,b2,X,Y)

dW1=grads['dW1']
db1=grads['db1']
dW2=grads['dW2']
db2=grads['db2']
# 参数更新
W1=W1-lr*dW1
b1=b1-lr*db1
W2=W2-lr*dW2
b2=b2-lr*db2

if i%100==0:
losses.append(loss)

if print_loss and i%100==0:
print('迭代次数:%d,误差值:%f'%(i+1,loss))

params={'W1':W1,
'b1':b1,
'W2':W2,
'b2':b2}

return params,losses

✌ 定义预测函数

上面optimizer函数会输出已经训练好的w、b参数,我们可以利用它们进行预测新的样本集
进行预测两个步骤:

  1. 【吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络、深度学习、机器学习_深度学习_23
  2. 利用概率将其转化为0-1类别
  3. 将结果存储到y_pred中
def predict(W1,b1,W2,b2,X,Y):
"""
功能:利用优化好的w和b值进行预测

参数:
W1:隐藏层的权值参数
b1:隐藏层的偏置参数
W2:输出层的权值参数
b2:输出层的偏置参数
X:特征矩阵
Y:标签

返回:
y_pred:预测值(1,样本数)维
"""
# 样本数
m=X.shape[1]
# 预测矩阵
y_pred=np.zeros((1,m))
# 一层激活
A1=np.tanh(np.dot(W1,X)+b1)
# 二层激活
A2=sigmoid(np.dot(W2,A1)+b2)
# 进行分类
for i in range(m):
y_pred[0,i]=1 if A2[0,i]>0.5 else 0

return y_pred

✌ 定义模型函数

我们已经将所需要的所有函数已经封装好了,现在需要一个训练函数调用它们,完成模型的训练,model的作用就是如此

def model(x_train,x_test,y_train,y_test,num_iter,lr,print_loss=False):
"""
功能:利用前面封装好的函数进行训练

参数:
x_train:训练数据(特征数,样本数)
x_test:初始化好的权重矩阵(特征数,样本数)
y_train:初始化好的权重矩阵(1,样本数)
y_test:初始化好的权重矩阵(1,样本数)
num_iter:梯度下降的迭代次数
lr:学习率
print_loss:是否每100次打印loss值

返回:
d:预测结果以w,b等数据
"""
# 获取各层的节点数
in_layer,hidden_layer,out_layer=layer_size(x_train,y_train)
# 初始化参数
init_params=init_w_b(in_layer,hidden_layer,out_layer)

W1=init_params['W1']
b1=init_params['b1']
W2=init_params['W2']
b2=init_params['b2']
# 梯度下降
params,losses=optimizer(W1,b1,W2,b2,x_train,y_train,num_iter,lr,print_loss)

W1=params['W1']
b1=params['b1']
W2=params['W2']
b2=params['b2']
# 预测训练集和测试集
y_pred_train=predict(W1,b1,W2,b2,x_train,y_train)
y_pred_test=predict(W1,b1,W2,b2,x_test,y_test)

print('训练集的准确性:%.3f'%((y_pred_train==y_train).sum()/y_train.shape[1]*100),'%')
print('测试集的准确性:%.3f'%((y_pred_test==y_test).sum()/y_test.shape[1]*100),'%')

d = {
"losses" : losses,
"y_pred_train" : y_pred_train,
"y_pred_test" : y_pred_test,
"W1" : W1,
"b1" : b1,
"W2":W2,
"b2":b2,
"learning_rate" :lr,
"num_iter" : num_iter }
return d

✌ 分割数据集

from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(X.T,np.squeeze(Y),test_size=0.2,random_state=2021)
x_train=x_train.T
x_test=x_test.T
y_train=np.array([y_train])
y_test=np.array([y_test])

✌ 进行测试

我们将我们处理好的训练测试集传入,测试下模型的效果,并每迭代100次打印下损失函数的值,观察模型效果是否得到了优化

print("====================测试model====================")     
d = model(x_train,x_test,y_train,y_test,3000,4.2,True)

查看输出结果:

====================测试model====================
迭代次数:1,误差值:0.915083
迭代次数:101,误差值:0.341307
迭代次数:201,误差值:0.314505
迭代次数:301,误差值:0.301869
迭代次数:401,误差值:0.293965
迭代次数:501,误差值:0.288318
迭代次数:601,误差值:0.283970
迭代次数:701,误差值:0.280468
迭代次数:801,误差值:0.277569
迭代次数:901,误差值:0.275129
迭代次数:1001,误差值:0.273056
迭代次数:1101,误差值:0.271282
迭代次数:1201,误差值:0.269751
迭代次数:1301,误差值:0.268418
迭代次数:1401,误差值:0.267231
迭代次数:1501,误差值:0.266127
迭代次数:1601,误差值:0.265013
迭代次数:1701,误差值:0.263849
迭代次数:1801,误差值:0.263058
迭代次数:1901,误差值:0.262612
迭代次数:2001,误差值:0.262124
迭代次数:2101,误差值:0.261580
迭代次数:2201,误差值:0.261060
迭代次数:2301,误差值:0.260638
迭代次数:2401,误差值:0.260392
迭代次数:2501,误差值:0.248556
迭代次数:2601,误差值:0.243686
迭代次数:2701,误差值:0.241531
迭代次数:2801,误差值:0.240559
迭代次数:2901,误差值:0.240050
训练集的准确性:90.000 %
测试集的准确性:91.250 %

✌ 测试简易神经网络(逻辑回归模型)

我们想要对比下加入了隐藏层和不加隐藏层的效果,我们需要建立一个不带隐藏层的神经网络,不过为了方便(懒得搭了),我们直接调用sklearn中的LogisticRegression库,其实是一样的,参数采取默认参数,如果参数调整一下,模型效果可能会更好

from sklearn.linear_model import LogisticRegression
clf=LogisticRegression()
score=cross_val_score(clf,X.T,np.squeeze(Y),cv=10)
for i,val in enumerate(score):
print('第%d折:%.3f'%(i+1,val))
print('Logistic模型平均效果:%.3f'%score.mean())

查看输出结果:

第1折:0.675
第2折:0.975
第3折:0.550
第4折:0.075
第5折:0.200
第6折:0.325
第7折:0.000
第8折:0.550
第9折:0.875
第10折:0.650
Logistic模型平均效果:0.487

✌ 测试LGB模型

import lightgbm as lgb
from sklearn.model_selection import cross_val_score

clf = lgb.LGBMClassifier(
boosting_type='gbdt', num_leaves=55, reg_alpha=0.0, reg_lambda=1,
max_depth=15, n_estimators=6000, objective='binary',
subsample=0.8, colsample_bytree=0.8, subsample_freq=1,
learning_rate=0.06, min_child_weight=1, random_state=20, n_jobs=4
)
score=cross_val_score(clf,X.T,np.squeeze(Y),cv=10)
for i,val in enumerate(score):
print('第%d折:%.3f'%(i+1,val))
print('LGB模型平均效果:%.3f'%score.mean())

查看输出结果:

第1折:0.425
第2折:0.850
第3折:0.825
第4折:0.900
第5折:0.925
第6折:0.975
第7折:0.875
第8折:0.700
第9折:0.775
第10折:0.525
LGB模型平均效果:0.778