编程要求:
在代码中不要使用循环,除非说明中明确告诉你需要使用。
编程目的:
编程环境:
python:3.7
Anaconda+pycharm
本次练习需要引入的lib:
numpy:是Python用于科学计算的基本包。
h5py:HDF5文件相当于一个容器,用于存储两类对象:datasets,类似于数组的数据集合;groups,类似于字典。
matplotlib:python用于画图的库。
PIL和scipy:scipy是一个用于数学、科学、工程领域的常用软件包,可以处理插值、积分、优化、图像处理、常微分方程数值解的求解、信号处理等问题。它用于有效计算numpy矩阵,使numpy和scipy协同工作,高效解决问题。
使用的数据集为data.h5
拿到一个数据集以后我们首先想到的就是如何查看这个数据集的信息,在新建的一个loadData.py里面我们实现其代码。
def load_dataset():
train_dataset = h5py.File('../dataSets/train_catvnoncat.h5', "r")
#查看数据集具体信息
for key in train_dataset.keys():
print(train_dataset[key].name)
print(train_dataset[key].shape)
运行结果:
由结果可以得出,在名为train_catvnoncat.h5数据文件里一共有三个数据表,名字与信息为:
数据表名 | 维数 |
---|---|
list_classes | (2,) |
train_set_x | (209,64,64,3) |
train_set_y | (209,) |
同理可以查看另一个数据文件中的数据表信息。
有了这些信息以后我们就可以进行数据处理。
```python
import numpy as np
import h5py
import matplotlib.pyplot as plt
import pylab
def load_dataset():
train_dataset = h5py.File('../dataSets/train_catvnoncat.h5', "r")
#查看数据集具体信息
#for key in train_dataset.keys():
# print(train_dataset[key].name)
# print(train_dataset[key].shape)
# print(train_dataset[key].value)
train_set_x_orig = np.array(train_dataset["train_set_x"][:])
train_set_y_orig = np.array(train_dataset["train_set_y"][:])
test_dataset = h5py.File('../dataSets/test_catvnoncat.h5', "r")
test_set_x_orig = np.array(test_dataset["test_set_x"][:])
test_set_y_orig = np.array(test_dataset["test_set_y"][:])
classes = np.array(test_dataset["list_classes"][:])
#将维数转换为(1,train_set_y_orig.shape[0]),可以理解为转置矩阵
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与test_set_x中的数据是RGB形式的数据,而matplotlib库里提供了让我们可以查看RGB数据图像形式的函数。
例如,查看d第2个标签的数据图像:
if __name__ == "__main__":
train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes = load_dataset()
index = 2
print(train_set_x_orig[index])
plt.imshow(train_set_x_orig[index])
plt.show()
在深度学习中很多错误都是由于维数错误导致的,现在我们将学习如何进行数据维数的转换。
目标:把图像数据的格式由(num_px,num_px,3)转换为数组格式(num_px×num_px×3,1).这样处理以后,训练集和数据集的格式就变成了一个二维矩阵,矩阵的每一行都代表一个进行了平滑操作的图像。
进行这一操作需要用到的主要代码是:
X_flattern = X.reshape(X.shape[0], -1).T
实现代码:
if __name__ == "__main__":
train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes = load_dataset()
#改变训练集和验证集数据维数
#reshape(m,-1)的格式就是将原来的数据转换成m行,比如原来是(4,2),reshape成(2,-1),那么新的数据格式就是(2,4)
#不可以直接写成下面的格式,表示固定列,求行,这样会使得矩阵意思发生改变,可以自己写一个矩阵试一试。实际上可以将正确的维数改变格式这样理解,每一行一定代表一个图片,所以我们要做的就是先固定行,再把RGB数据乘起来。这样每一行还是一个图片。
# train_set_x_flatten = train_set_x_orig.reshape(-1, train_set_x_orig.shape[0])
train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
print("train_set_x_flatten shape:" + str(train_set_x_flatten.shape))
print("train_set_y shape:" + str(train_set_y_orig.shape))
print("test_set_x_flatten shape:" + str(test_set_x_flatten.shape))
print("test_set_y shape:" + str(test_set_y_orig.shape))
机器学习中一个常见的预处理步骤是对数据集进行集中和标准化,这意味着从每个示例中减去整个numpy数组的平均值,然后用整个numpy数组的标准差除以每个示例。但是对于图像数据集来说,将数据集的每一行除以255(像素通道的最大值)会更简单、更方便,而且效果几乎一样。
train_set_x = train_set_x_flatten / 255
test_set_x = test_set_x_flatten / 255
预处理数据集的一般步骤:
1.找到问题的大小和数据的维数。
2.重新定义数据集大小,使每个向量成为(num_px×num_px×3,1)。
3.标准化数据。
接下来我们就实现一个简单的算法来识别猫和非猫图片。
我将建立一个逻辑回归模型,实际上下面的式子就可以证明逻辑回归实际上是一个非常简单的神经网络模型。
对于 x ( i ) x^{(i)} x(i):
z ( i ) z^{(i)} z(i) = w T w^{T} wT x ( i ) x^{(i)} x(i) + b b b
y ^ ( i ) \hat{y}^{(i)} y^(i) = a ( i ) a^{(i)} a(i) = s i g m o i d sigmoid sigmoid( z ( i ) z^{(i)} z(i))
L ( a ( i ) , y ( i ) L(a^{(i)},y^{(i)} L(a(i),y(i) = − y ( i ) log ( a ( i ) ) − ( 1 − y ( i ) ) log ( 1 − a ( i ) ) -y^{(i)}\log(a^{(i)}) - (1 - y^{(i)})\log(1 - a^{(i)}) −y(i)log(a(i))−(1−y(i))log(1−a(i))
代价函数:
J = 1 m ∑ i = 1 m L ( a ( i ) , y ( i ) ) J = {1 \over m}\sum_{i=1}^{m}L(a^{(i)},y^{(i)}) J=m1∑i=1mL(a(i),y(i))
1.定义模型结构(例如输入特征的数量)
2.初始化模型参数
3.循环:
def sigmoid(z):
s = 1 / (1 + np.exp(-z))
return s
def initialize_with_zeros(dim):
"""
函数用来初始化权重w,b
:param dim: 权重的维数
:return: w,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):
#前向传播
m = X.shape[1]
A = sigmoid.sigmoid(np.dot(w.T, X) + b)
loss = -(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)
#从数组的形状中删除单维度条目,即把shape中为1的维度去掉
loss = np.squeeze(loss)
assert (loss.shape == ())
grads = {"dw":dw ,"db":db}
return grads, loss
if __name__ == "__main__":
w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1, 2], [3, 4]]), np.array([[1, 0]])
grads, loss = propagate(w, b, X, Y)
grads, cost = propagate(w, b, X, Y)
print("dw = " + str(grads["dw"]))
print("db = " + str(grads["db"]))
print("cost = " + str(cost))
到现在我们已经初始化了参数,也计算出了代价函数和其梯度,现在我们要做的就是利用梯度信息更新这些参数。
**目标:**实现最优化函数,学习到能使代价函数最小的参数w和b。
代价更新公式:对于参数 θ \theta θ, θ \theta θ = θ − a d θ \theta-ad\theta θ−adθ ,a是学习率。
代码:
def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
"""
找到使得代价函数最小的参数w,b
:param w:
:param b:
:param X:
:param Y:
:param num_iterations: 迭代次数
:param learning_rate: 学习率
:param print_cost: 每100次打印一次代价值
:return:
params:包含权重w和偏差b的字典
grads:包含相对于成本函数的梯度字典的权重和偏差
costs:优化期间全部的代价值(100次记录一次),用于绘制代价曲线
"""
costs = []
for i in range(num_iterations):
grads, cost = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
w = w - learning_rate * dw
b = b - learning_rate * db
if i % 100 == 0:
costs.append(cost)
if print_cost and i % 100 == 0:
print("第%i次迭代后的代价为:%f" %(i, float(cost)))
params = {"w": w,
"b": b}
grads = {"dw": dw,
"db": db}
return params, grads, costs
调用:
params, grads, costs = optimize(w, b, X, Y, num_iterations=100, learning_rate= 0.009, print_cost=True)
print("w = " + str(params["w"]))
print("b = " + str(params["b"]))
print("dw = " + str(grads["dw"]))
print("db = " + str(grads["db"]))
先前我们已经实现了最优化的函数,也就意味着可以计算出能使代价函数最小的参数w,b,进而我们就可以用求出的参数预测结果。
1. y ^ \hat{y} y^ = A A A = σ \sigma σ( W T W^{T} WT x x x + b b b)
2.预测值如果<=0.5就预测为0,如果>0.5就预测为1;将预测值存在向量Y_prediction.
def predict(w, b, X):
#计算出X的列数,也就是得出预测样本个数
m = X.shape[1]
Y_prediction = np.zeros((1, m))
w = w.reshape(X.shape[0], 1)
A = sigmoid.sigmoid(np.dot(w.T, X) + b)
for i in range(A.shape[1]):
Y_prediction[0, i] = 1 if A[0, i] > 0.5 else 0
assert(Y_prediction.shape == (1, m))
return Y_prediction
def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5, print_cost = False):
#1.初始化所有的参数
w, b = initialize_with_zeros(X_train.shape[0])
#2.梯度下降算法
paramters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)
#3.获得最优参数
w = paramters["w"]
b = paramters["b"]
#4.进行预测
Y_prediction_test = predict(w, b, X_test)
Y_prediction_train = predict(w, b, X_train)
#5.打印训练集和测试集的错误率
print("训练集精度:{}%".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
print("测试集: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))
d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train": Y_prediction_train,
"w": w,
"b": b,
"learning_rate": learning_rate,
"num_iterations": num_iterations}
return d
整合调用:
train_set_x, train_set_y, test_set_x, test_set_y, classes = loadData.load_dataset()
d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations=2000, learning_rate=0.005,
print_cost=True)
运行结果:
训练集准确度到了99%以上,而测试集精度只有70%,可以分析出这个模型存在过拟合问题。
代码:
cost = np.squeeze(d['costs'])
plt.plot(cost)
plt.ylabel('cost')
plt.xlabel('iterations (per hundreds)')
plt.title("Learning rate=" + str(d["learning_rate"]))
plt.show()
从图像可以分析出随着迭代次数的增加,代价逐渐减小,这也意味着参数是可以通过学习不断改变的。
尝试使用不同的学习率进行分析
代码:
learning_rates = [0.01, 0.001, 0.0001]
models = {}
for i in learning_rates:
print("learning rate is:" + str(i))
models[str(i)] = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations=1500, learning_rate=i, print_cost=False)
print('\n' + "-------------------------------------------------------" + '\n')
for i in learning_rates:
plt.plot(np.squeeze(models[str(i)]["costs"]), label=str(models[str(i)]["learning_rate"]))
plt.ylabel('cost')
plt.xlabel('iterations')
legend = plt.legend(loc='upper center', shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
plt.show()
运行结果:
通过上图我们可以分析出不同的学习率有不同的学习效果,进而也会产生不同的预测结果。学习率较大,代价起伏也会较大,低成本并不意味着好的模式。当训练精度远高于测试精度时,我们就要考虑是否存在过拟合现象。
代码:
my_image = "cat_mine.jpg"
fname = "../dataSets/" + my_image
image = np.array(ndimage.imread(fname, flatten=False))
my_image = scipy.misc.imresize(image, size=(64, 64)).reshape((1, 64 * 64 * 3)).T
my_predicted_image = predict(d["w"], d["b"], my_image)
plt.imshow(image)
print("y = " + str(np.squeeze(my_predicted_image)) + ", your algorithm predicts a \"" + classes[
int(np.squeeze(my_predicted_image)),].decode("utf-8") + "\" picture.")