本文所使用的资料已上传到百度网盘-网盘链接,请在开始之前下载好所需资料,然后将文件解压到你的代码文件同一级目录下,请确保你的代码那里有lr_utils.py和datasets文件夹。
做一个识别猫的神经网络,希望可以动手一步一步实现。
导入所需的库:
- numpy :是用Python进行科学计算的基本软件包。
- h5py:是与H5文件中存储的数据集进行交互的常用软件包。
- matplotlib:是一个著名的库,用于在Python中绘制图表。
- lr_utils :在本文的资料包里,一个加载资料包里面的数据的简单功能的库。
import h5py
import numpy as np
import matplotlib.pyplot as plt
from lr_utils import load_dataset
可以查看lr_utils.py文件中的内容。
f = open('lr_utils.py','r')
print(f.read())
内容如下:
import numpy as np
import h5py
def load_dataset():
train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels
test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels
classes = np.array(test_dataset["list_classes"][:]) # the list of classes
train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes
train_set_x_orig :保存的是训练集里面的图像数据(本训练集有209张64x64的图像)。
train_set_y_orig :保存的是训练集的图像对应的分类值(【0 | 1】,0表示不是猫,1表示是猫)。
test_set_x_orig :保存的是测试集里面的图像数据(本训练集有50张64x64的图像)。
test_set_y_orig : 保存的是测试集的图像对应的分类值(【0 | 1】,0表示不是猫,1表示是猫)。
classes : 保存的是以bytes类型保存的两个字符串数据,数据为:[b’non-cat’ b’cat’]。
我们可以看一下我们加载的文件里面的图片都是些什么样子的,比如我就查看一下训练集里面的第26张图片,当然你也可以改变index的值查看一下其他的图片。
index = 25
plt.imshow(train_set_x_orig[index])
你也可以查看一下训练集的标签是什么样子的
[[0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0
0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 1 0 0 1
0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 1 1 0 0 1 0 0 0 0 1 0 1 0 1 1
1 1 1 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0
1 1 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 0 0 1 1 0 1 0 1
0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0]]
我们可以结合训练集查看我们加载的东西。
print("y = " + str(train_set_y[:,index]) + ",it 's a " + classes[np.squeeze(train_set_y[:,index])].decode("utf-8") + " picture")
输出如下:
y = [1],it 's a cat picture
- m_train :训练集里图片的数量。
- m_test :测试集里图片的数量。
- num_px : 训练、测试集里面的图片的宽度和高度(均为64x64)。
m_train = train_set_y.shape[1]
m_test = test_set_y.shape[1]
num_px = train_set_x_orig.shape[1]
print('训练集图片数量m_train = '+str(m_train))
print('测试集图片数量m_test = '+str(m_test))
print('图片的大小:'+str(num_px)+','+str(num_px))
print('训练集图片维数:'+str(train_set_x_orig.shape))
print('训练集标签维数:'+str(train_set_y.shape))
输出如下:
训练集图片数量m_train = 209
测试集图片数量m_test = 50
图片的大小:64,64
训练集图片维数:(209, 64, 64, 3)
训练集标签维数:(1, 209)
为了方便,我们要把维度为(64,64,3)的numpy数组重新构造为(64 x 64 x 3,1)的数组,要乘以3的原因是每张图片是由64x64像素构成的,而每个像素点由(R,G,B)三原色构成的,所以要乘以3。在此之后,我们的训练和测试数据集是一个numpy数组,【每列代表一个平坦的图像】 ,应该有m_train和m_test列。
当你想将形状(a,b,c,d)的矩阵X平铺成形状(b * c * d,a)的矩阵X_flatten时,可以使用以下代码:
细节扩展:
矩阵(a,b,c,d)采取从右往左读法,例如(1,2,3,4)首先为(3,4)矩阵为一整体,然后(2,(3,4))矩阵当一整体,最后为(1,(2,(3,4)))这样就更容易理解矩阵的形状啦。
代码如下:
x = np.random.randn(1,2,3,4)
print(x)
print('-'*60)
print(x.shape[0])
x_f = x.reshape(x.shape[0],-1)
print(x_f.shape)
x_f = x.reshape(x.shape[0],-1).T
print(x_f.shape)
print(x_f)])
输出如下:
[[[[-1.5019957 0.27460599 -0.13503757 0.61359487]
[ 0.43779331 1.8275767 -1.35215534 0.37215807]
[ 1.20063894 1.57175417 -1.68714452 -2.98496014]]
[[-1.42841035 -0.36910962 -0.05694828 1.48283783]
[ 1.52399195 -0.69201781 0.15564753 0.32705825]
[ 0.38743142 0.63207278 0.20439947 -0.29619398]]]]
------------------------------------------------------------
1
(1, 24)
(24, 1)
[[-1.5019957 ]
[ 0.27460599]
[-0.13503757]
[ 0.61359487]
[ 0.43779331]
[ 1.8275767 ]
[-1.35215534]
[ 0.37215807]
[ 1.20063894]
[ 1.57175417]
[-1.68714452]
[-2.98496014]
[-1.42841035]
[-0.36910962]
[-0.05694828]
[ 1.48283783]
[ 1.52399195]
[-0.69201781]
[ 0.15564753]
[ 0.32705825]
[ 0.38743142]
[ 0.63207278]
[ 0.20439947]
[-0.29619398]]
回归正文:
#将训练集的维度降低并转置。
train_set_x_orig_f = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T
#将测试集的维度降低并转置。
test_set_x_orig_f = test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).T
print("训练集的维度降低并转置后的维度:"+str(train_set_x_orig_f.shape))
print("测试集的维度降低并转置后的维度:"+str(test_set_x_orig_f.shape))
输出如下:
训练集的维度降低并转置后的维度:(12288, 209)
测试集的维度降低并转置后的维度:(12288, 50)
建立神经网络的主要步骤是:
1. 定义模型结构(例如输入特征的数量)
2. 初始化模型的参数
3. 循环:
- 计算当前损失(正向传播)
- 计算当前梯度(反向传播)
- 更新参数(梯度下降)
现在构建sigmoid()函数,需要使用sigmoid(w^T x +b)来实现。
def sigmoid(z):
"""
z 为任意大小的标量或者numpy 数组
"""
s = 1 / (1+ np.exp(-z))
return s
测试:
print("="*20+"测试sigmoid()"+"="*20)
print("sigmoid(0)="+str(sigmoid(0)))
print("sigmoid(9.2)"+str(sigmoid(9.2)))
输出:
====================测试sigmoid()====================
sigmoid(0)=0.5
sigmoid(9.2)0.9998989708060922
初始化w,b
def initial_wb_zero(dim):
"""
此函数为w初始化维度为dim,1)的 0 向量,并将b 初始化为0
返回:
初始化的 w and b
"""
w = np.zeros(shape = (dim,1))
b = 0
assert (w.shape == (dim,1))
assert (isinstance(b,float) or isinstance(b,int))
return (w,b)
计算成本和渐变函数的实现:
def propagate(w,b,X,Y):
"""
实现前向和后向的传播的成本函数和梯度。
参数:
w - 权重,大小不等的数组(num_px * num_px * 3,1)
b - 偏差,一个标量
X - 矩阵类型(num_px * num_px * 3,训练数量)
Y - 标签矢量(不是猫为0,是猫为1),维度(1,训练集数量)
返回:
cost - 逻辑回归的负对数似然成本。
dw - 相对于w损失梯度,因此与w相同的形状
db - 相对于b损失梯度,因此与b相同的形状
"""
m = X.shape[1]
# 正向传播
A = sigmoid(np.dot(w.T,X) + b) #计算激活值
cost = (-1/m) * np.sum(Y * np.log(A) + (1-Y)*(np.log(1-A))) #计算成本
# 反向传播
dw = (1/m)*np.dot(X,(A-Y).T)
db = (1/m)*np.sum(A-Y)
# 确认数据
assert (dw.shape == w.shape)
assert (db.dtype == float)
cost = np.squeeze(cost)
assert (cost.shape == ())
grads = {"dw":dw , "db":db}
return (grads,cost)
测试:
w ,b ,X ,Y = np.array([[1],[2]]) , 2 ,np.array([[1,2],[3,4]]),np.array([[1,0]])
grads , cost = propagate(w,b,X,Y)
print("dw="+str(grads["dw"]))
print("db="+str(grads["db"]))
print("cost="+str(cost))
输出:
dw=[[0.99993216]
[1.99980262]]
db=0.49993523062470574
cost=6.000064773192205
目标是通过最小化成本函数 J 来学习 w和b 。对于参数 lr ,更新规则是 θ=θ− lr dθ,其中 lr 是学习率。
def optimize(w,b,X,Y,max_iter,lr,out_cost = False):
"""
此函数是用来优化w and b
参数:
w - 权重,大小不等的数组(num_px * num_px * 3,1)
b - 偏差,一个标量
X - 矩阵类型(num_px * num_px * 3,训练数量)
Y - 标签矢量(不是猫为0,是猫为1),维度(1,训练集数量)
max_iter - 迭代次数
lr - 学习率
out_cost - 打印损失值
返回:
params - 包含w and b的字典
grads - 包含权重和偏差相对于成本函数的梯度的字典
costs - 优化期间的成本列表
"""
costs = []
for i in range(max_iter):
grads , cost = propagate(w,b,X,Y)
dw , db = grads['dw'] , grads['db']
w = w - lr*dw
b = b - lr*db
# 记录成本
if i % 100 ==0:
costs.append(cost)
# if (out_cost) and (i%100==0):
# print('iter number: '+str(i) + ', deviation: '+str(cost))
params = { "w": w , "b" : b }
grads = {"dw": dw , "db" : db }
return (params,grads,costs)
测试:
w ,b ,X ,Y = np.array([[1],[2]]) , 2 ,np.array([[1,2],[3,4]]),np.array([[1,0]])
params , grads , costs = optimize(w,b,X,Y,max_iter=100,lr=0.01,out_cost=False)
print("w="+str(params["w"]))
print("b="+str(params["b"]))
print("dw="+str(grads["dw"]))
print("db="+str(grads["db"]))
输出:
w=[[0.02729685]
[0.06634584]]
b=1.5195244996409367
dw=[[0.80147827]
[1.53224345]]
db=0.36538259061840267
optimize函数会输出已学习的w和b的值,我们可以使用w和b来预测数据集X的标签。
现在我们要实现预测函数predict()。
然后将预测值存储在向量predict_nums中。
def predict(w,b,X):
"""
此函数用于预测标签是0 or 1
返回:
pred_nums - 包含所有图片的的所有预测 0 | 1 的值
"""
m = X.shape[1]
pred_nums = np.zeros((1,m))
w = w.reshape(X.shape[0],1)
# 计算预测猫在图片中出现的概率
A = sigmoid(np.dot(w.T,X)+b)
for i in range(A.shape[1]):
pred_nums[0,i] = 1 if A[0,i] > 0.5 else 0
assert (pred_nums.shape == (1,m))
return pred_nums
测试:
w ,b ,X ,Y = np.array([[1],[2]]) , 2 ,np.array([[1,2],[3,4]]),np.array([[1,0]])
pred_nums = predict(w,b,X)
print(pred_nums)
输出:
[[1. 1.]]
让我们进一步分析一下,并研究学习率alpha的可能选择。为了让渐变下降起作用,我们必须明智地选择学习速率。学习率αα 决定了我们更新参数的速度。如果学习率过高,我们可能会“超过”最优值。同样,如果它太小,我们将需要太多迭代才能收敛到最佳值。这就是为什么使用良好调整的学习率至关重要的原因。
我们可以比较一下我们模型的学习曲线和几种学习速率的选择。也可以尝试使用不同于我们初始化的learning_rates变量包含的三个值,并看一下会发生什么。
def model(x_train,y_train,x_test,y_test,max_iter=2000,lr = 0.5,out_cost=False):
"""
参数:
x_train - (num_px*num_px*3,m_train)训练集
y_train - (1,m_train)训练标签集
x_test - (num_px*num_px*3,m_test)测试集
y_test - (1,m_test)测试标签集
max_iter -优化迭代次数的超参数
lr - 学习率
out_cost - 为True打印成本
返回:
dictor - 有关模型的字典
"""
w , b = initial_wb_zero(x_train.shape[0])
params , grads ,costs = optimize(w,b,x_train,y_train,max_iter,lr,out_cost)
w , b = params["w"] , params["b"]
predict_test = predict(w,b,x_test)
predict_train = predict(w,b,x_train)
print("Test Accuracy {}".format(100-np.mean(np.abs(predict_test-y_test))*100),'%')
print("Train Accuracy {}".format(100-np.mean(np.abs(predict_train-y_train))*100),'%')
dictor = { "costs": costs ,
"predict_test": predict_test ,
"predict_train": predict_train ,
"w": w,
"b": b,
"max_iter": max_iter ,
"lr": lr}
# costs = np.squeeze(dictor['costs'])
# plt.plot(costs)
# plt.ylabel('costs')
# plt.xlabel('itertions')
# plt.title("Learning rate = "+str(lr))
# plt.show()
return dictor
# dictor = model(train_set_x,train_set_y,test_set_x,test_set_y,max_iter=2000,lr=0.005,out_cost=True)
lrs = [0.005,0.01,0.001]
models ={}
for i in lrs:
print("Learning rate: "+str(i))
models[str(i)] = model(train_set_x,train_set_y,test_set_x,test_set_y,max_iter=2000,lr= i,out_cost=True)
print('\n'+'-'*40+'\n')
for i in lrs:
plt.plot(np.squeeze(models[str(i)]['costs']))
plt.ylabel('costs')
plt.xlabel('iterations')
legend = plt.legend(loc ='upper center',shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
plt.show()
输出:
Learning rate: 0.005
Test Accuracy 70.0 %
Train Accuracy 99.04306220095694 %
----------------------------------------
Learning rate: 0.01
Test Accuracy 70.0 %
Train Accuracy 99.52153110047847 %
----------------------------------------
Learning rate: 0.001
No handles with labels found to put in legend.
Test Accuracy 68.0 %
Train Accuracy 91.38755980861244 %
到这里逻辑回归就完整的实现了,不知道大家有没有看懂,欢迎留言!
注:以上代码全部在Jupyter notebook执行的,我的上传资源里会把代码文件上传,有需要的下载即可。
本文参考:https://blog.csdn.net/u013733326/article/details/79639509