具有神经网络思维的Logistic回归
一、目的
搭建一个能够识别猫的简单的神经网络
二、训练集与测试集
百度网盘,提取码:1234
三、编程
注:本编程未使用提供的py文件——lr_utils.py
本次编程所需的所有模块
import h5py
import numpy as np
import matplotlib.pyplot as plt
from skimage import transform
numpy :是用Python进行科学计算的基本软件包;
h5py:是与H5文件中存储的数据集进行交互的常用软件包;
matplotlib:是一个著名的库,用于在Python中绘制图表;
skimage:在本次编程用来修改图片的维度
1.导入原始数据集进行剖析
将训练集保存到train_dataset
,测试集保存到test_dataset
# 导入原始数据集
train_dataset = h5py.File("./datasets/train_catvnoncat.h5", "r")
test_dataset = h5py.File("./datasets/test_catvnoncat.h5", "r")
使用for循环查看训练集中的键:
for key in train_dataset.keys():
print(key)
训练集中我们可以得到三个键:
train_set_x
:保存的是训练集里面的图像数据;
train_set_y
:保存的是训练集的图像对应的分类值(0 | 1,0表示不是猫,1表示是猫);
list_classes
: 保存的是以bytes类型保存的两个字符串数据,数据为:b’non-cat’ b’cat’]
测试集中是test_set_x
,test_set_y
,list_classes
这些数据都可以用print函数打印出来查看
我们单独将训练集和测试集的原始数据和标签导出,使用x代表数据,y代表标签
# 单独导出训练集和测试集的原始数据和标签
train_set_x_orig = np.array(train_dataset["train_set_x"][:])
train_set_y_orig = np.array(train_dataset["train_set_y"][:])
test_set_x_orig = np.array(test_dataset["test_set_x"][:])
test_set_y_orig = np.array(test_dataset["test_set_y"][:])
我们可以选择任意一个样本查看一下是什么样的,以及标签
# 查看第三张图片和标签
plt.imshow(train_set_x_orig[2])
print(train_set_y_orig[2])
plt.show()
我们再查看一下数据的维度
print(train_set_x_orig.shape, train_set_y_orig.shape,
test_set_x_orig.shape, test_set_y_orig.shape)
(209, 64, 64, 3)表示训练集有209张rgb图片,每个通道图片的大小是64*64的;(209,)表示标签的形状;也能看到测试集有50张图片
2.处理数据的维度
这一步非常重要,要将数据的维度处理成为神经网络输入的维度,很多问题都是由于维度没有处理好导致的。
现在我们数据的维度是:
数据集 | 原始维度 |
---|---|
train_set_x_orig | (209, 64, 64, 3) |
train_set_y_orig | (209,) |
test_set_x_orig | (50, 64, 64, 3) |
test_set_y_orig | (50,) |
现在让我们来回顾一下神经网络的前向传播是怎样的:
对于某一个训练样本i输入它的n个特征值,每个特征值乘上权重再加上b,我们将其结果记为
z
(
i
)
z^{(i)}
z(i),即
z
(
i
)
=
x
i
1
w
1
+
x
i
2
w
2
+
.
.
.
+
x
i
n
w
n
+
b
z^{(i)}=x_{i1}w_1+x_{i2}w_2+...+x_{in}w_n+b
z(i)=xi1w1+xi2w2+...+xinwn+b,经过激活函数a,就可以得到预测值,现在我们若有m个样本,每个样本具有n个特征,我们就能得到m个z,则我们需要对其进行向量化,去掉for循环,加快程序运行速度
由公式
Z
=
w
T
X
+
b
Z=w^TX+b
Z=wTX+b得:
那么对于本次数据集我们就可以得到我们需要的维度:
数据集 | 原始维度 | 需要的维度 |
---|---|---|
train_set_x_orig=X | (209, 64, 64, 3) | (12288,209) |
train_set_y_orig= y ^ \hat{y} y^ | (209,) | (1, 209) |
test_set_x_orig | (50, 64, 64, 3) | (12288,50) |
test_set_y_orig | (50,) | (1, 50) |
原始文档在此
现在我们开始处理数据维度,在此期间可以多次使用shape查看维度是否正确
# 处理数据的维度
m = train_set_x_orig.shape[0]
train_set_x_tran = train_set_x_orig.reshape((m, -1)).T
train_set_y_tran = train_set_y_orig.reshape((m, -1)).T
m = test_set_x_orig.shape[0]
test_set_x_tran = test_set_x_orig.reshape((m, -1)).T
test_set_y_tran = test_set_y_orig.reshape((m, -1)).T
查看数据的维度是否处理的正确
print(train_set_x_tran.shape, train_set_y_tran.shape, test_set_x_tran.shape, test_set_y_tran.shape)
3.标准化数据
对数据进行标准化,因为在图片中,一个数据的大小在0-255之间,我们可以选第一个样本的部分数据来看一下
print(train_set_x_tran[:9, :9])
这会导致不同的特征的权重差别比较大,通常会将数据处理在一个比较小的范围内,如0-1之间,防止不同特征之间权重比例失衡;我们对样本每个特征都除(max-min)=255即可
# 标准化数据
train_set_x_stan = train_set_x_tran / 255
test_set_x_stan = test_set_x_tran / 255
再看一下处理过后的数据
4.定义激活函数,初始化w和b
这里激活函数我们选用sigmoid函数,函数定义为:
A
=
s
i
g
(
Z
)
=
g
(
Z
)
=
1
1
+
e
−
Z
A=sig(Z)=g(Z)=\frac{1}{1+e^{-Z}}
A=sig(Z)=g(Z)=1+e−Z1
# 定义sigmoid函数
def sigmoid(Z):
s = 1 / (1 + np.exp(-Z))
return s
初始化w和b
# 初始化w和b,w为n*1的数组,b为常数
n_dim = train_set_x_stan.shape[0]
w = np.zeros((n_dim, 1))
b = 0
5.定义前向传播函数、代价函数以及反向传播函数
我们再来看一下神经网络的反向传播,就是利用我们得到的代价损失函数L返回去去推导w和b
损失函数L和代价函数J
想看如何推导的,链接在此:001_wz_sf_逻辑回归(Logistic Regression)
使用梯度下降法推导dw和db
再对其向量化,最后就可以得到w和b以及损失函数J的迭代公式
5.1 前向传播函数
Z = w T X + b Z = w^TX+b Z=wTX+b A = s i g m o i d ( Z ) A = sigmoid(Z) A=sigmoid(Z)
5.2 代价函数
J = − 1 m ∑ i = 1 m ( y ∗ l o g A + ( 1 − y ) ∗ l o g ( 1 − A ) ) J=-\frac{1}{m}\sum{^{m}_{i=1}}(y*logA+(1-y)*log(1-A)) J=−m1∑i=1m(y∗logA+(1−y)∗log(1−A))
5.3 反向传播函数
d w = 1 m X ( A − y ) dw=\frac{1}{m}X(A-y) dw=m1X(A−y) d b = 1 m ∑ i = 1 m ( A − y ) db=\frac{1}{m}\sum{^m_{i=1}}(A-y) db=m1∑i=1m(A−y)
# 定义前向传播函数、代价函数以及反向传播函数
def propagate(w, b, X, y, alpha):
# 1.前向传播函数
Z = np.dot(w.T, X) + b
A = sigmoid(Z)
# 2.代价函数
m = train_set_x_stan.shape[1]
J = -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)
grands = {"dw": dw,
"db": db}
return grands, J
最后将dw,db,J返回
6.进行优化
迭代公式
w
:
=
w
−
α
d
w
w := w-\alpha dw
w:=w−αdw
b
:
=
b
−
α
d
b
b:=b-\alpha db
b:=b−αdb
设计一个成本列表,用于储存损失函数的数值,后面用于绘图,然后返回各个需要用到的参数
注:形参print_cost是一个控制间隔100次打印成本的布尔类型参数
# 优化部分
def optimize(w, b, X, y, alpha, n_iters, print_cost):
costs = []
for i in range(n_iters):
grands, J = propagate(w, b, y, X)
dw = grands["dw"]
db = grands["db"]
w = w - alpha * dw
b = b - alpha * db
if i % 100 == 0:
costs.append(J)
if print_cost:
print("n_iters is", i, "cost is", J)
grands = {"dw": dw, "db": db}
params = {"w": w, "b": b}
return grands, params, costs
7.进行预测
传入测试集X_test
,进行预测输出预测结果y_pred
# 预测部分
def predict(w, b, X_test):
Z = np.dot(w.T, X_test)
A = sigmoid(Z)
m = X_test.shape[1]
y_pred = np.zeros((1, m))
for i in range(m):
if A[:, i] > 0.5:
y_pred[:, i] = 1
else:
y_pred[:, i] = 0
return y_pred
8.模型整合
模型的整合,方面后面模型的调整
# 模型的整合
def model(w, b, X_train, y_train, X_test, y_test, alpha, n_iters, print_cost):
grands, params, costs = optimize(w, b, X_train, y_train, alpha, n_iters, print_cost)
w = params["w"]
b = params["b"]
y_train_pred = predict(w, b, X_train)
y_test_pred = predict(w, b, X_test)
print("alpha is", alpha)
print("the train acc is", np.mean(y_train_pred == y_train) * 100, "%")
print("the test acc is", np.mean(y_test_pred == y_test) * 100, "%")
print("==============================")
b = {
"w": w,
"b": b,
"y_train_pred": y_train_pred,
"y_test_pred": y_test_pred,
"costs": costs,
"alpha":alpha
}
return b
9.运行与结果分析
取学习速率(步长) α \alpha α=0.005,迭代次数n_iters=2000次,运行结果为在训练集上正确率99%,测试集上70%
d = model(w, b, train_set_x_stan, train_set_y_tran, test_set_x_stan, test_set_y_tran, alpha=0.005, n_iters=2000, print_cost=False)
学习成本的变化如下图
对于学习速率
α
\alpha
α的取值,
α
\alpha
α决定了我们更新参数w和b的速度,如果
α
\alpha
α取值过大,我们得到的值可能会“超过”最优值;若
α
\alpha
α取值过小,我们需要迭代太多次才能熟练到最优值,所以
α
\alpha
α的取值至关重要,下面我们来看一下不同
α
\alpha
α值的成本曲线:
# alpha的不同取值
alphas = [0.01, 0.001, 0.0001]
for alpha in alphas:
d = model(w, b, train_set_x_stan, train_set_y_tran, test_set_x_stan, test_set_y_tran,
alpha=alpha, n_iters=2000, print_cost=False)
plt.plot(d["costs"], label=str(alpha))
plt.xlabel("per hundred iters")
plt.ylabel("cost")
plt.legend()
plt.show()
在相同的迭代次数下,可以明显看到在学习速率比较小时,整个成本下降的速度是越来越慢的,收敛到最优值是比较慢的;而对于比较大的学习速率,可见其收敛的速度很快,但是其在前面错过了一些局部最优值,所有前期曲线会有一部分上升,但是总体是在下降的。
可以参见文章:机器学习算法:梯度下降法——原理篇
10.测试
我们可以自己选择一张图片测试
读入要预测的图片,查看维度,将其改为我们所需要的维度,使用预测函数预测
# 读入图片
fname = "E:\py\DL\datasets\img1.jpg"
img_org = plt.imread(fname)
# print(img_org.shape)
# 修改维度
img_tran = transform.resize(img_org, (64, 64, 3)).reshape(64*64*3, 1)
#print(img_tran.shape)
# 进行预测
pred = predict(d["w"], d["b"], img_tran)
print(int(pred))
plt.imshow(img_org)
plt.show()
可以看到预测结果是1,预测成功了
11.源码
四、总结
整个神经网络的架构如下
在整个过程中,要搞清楚这里面每一步做的都是为了什么,以及参数w和b在循环:计算当前损失(正向传播)–>计算当前梯度(反向传播)–>更新参数(梯度下降)中是如何求得的,这是比较关键的,还有一个是一定要计算好数据的维度,到哪一步得到的数据应该是什么维度。
五、参考文章
【中文】【吴恩达课后编程作业】Course 1 - 神经网络和深度学习 - 第二周作业
机器学习算法:梯度下降法——原理篇
特别感谢:B站UP主ladykaka007的视频