在开始之前,我们有需要引入的库:
import numpy as np import copy import matplotlib.pyplot as plt import h5py import scipy from PIL import Image from scipy import ndimage from lr_utils import load_dataset from public_tests import * %matplotlib inline %load_ext autoreload %autoreload 2
问题陈述:有一个训练集和测试集,其中有被标记为猫和非猫的数据,要以此构建一个简单的图像识别算法。
加载数据
# Loading the data (cat/non-cat) train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()
在图像数据集(训练和测试)的末尾添加了“_orig”是为了对它们进行预处理。
我们来随机查看一下训练集中的图片。
# Example of a picture index = 25 plt.imshow(train_set_x_orig[index]) print ("y = " + str(train_set_y[:, index]) + ", it's a '" + classes[np.squeeze(train_set_y[:, index])].decode("utf-8") + "' picture.")
输出的结果为
已知数据集中参数:
提示:
train_set_x_orig 是一个维度为(m_train,num_px,num_px,3)的数组。
可以通过 train_set_x_orig.shape[0] 得到 m_train。
#(≈ 3 lines of code) # m_train = # m_test = # num_px = # YOUR CODE STARTS HERE m_train = train_set_x_orig.shape[0] m_test = test_set_x_orig.shape[0] num_px = train_set_x_orig.shape[1] # YOUR CODE ENDS HERE print ("Number of training examples: m_train = " + str(m_train)) print ("Number of testing examples: m_test = " + str(m_test)) print ("Height/Width of each image: num_px = " + str(num_px)) print ("Each image is of size: (" + str(num_px) + ", " + str(num_px) + ", 3)") print ("train_set_x shape: " + str(train_set_x_orig.shape)) print ("train_set_y shape: " + str(train_set_y.shape)) print ("test_set_x shape: " + str(test_set_x_orig.shape)) print ("test_set_y shape: " + str(test_set_y.shape))
输出结果
将维度为 (num_px, num_px, 3)的训练集和测试集数组平铺为(num_px ∗ num_px ∗ 3, 1)。
提示:
当你想将形状(a,b,c,d)的矩阵X平铺成形状(b * c * d,a)的矩阵X_flatten时,可以使用以下代码:
X_flatten = X.reshape(X.shape[0], -1).T # X.T is the transpose of X #reshape(m,-1) 改变维度为m行d列,-1表示列数自动计算
因此有
# Reshape the training and test examples #(≈ 2 lines of code) # train_set_x_flatten = ... # test_set_x_flatten = ... # YOUR CODE STARTS HERE 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 # YOUR CODE ENDS HERE # Check that the first 10 pixels of the second image are in the correct place assert np.alltrue(train_set_x_flatten[0:10, 1] == [196, 192, 190, 193, 186, 182, 188, 179, 174, 213]), "Wrong solution. Use (X.shape[0], -1).T." assert np.alltrue(test_set_x_flatten[0:10, 1] == [115, 110, 111, 137, 129, 129, 155, 146, 145, 159]), "Wrong solution. Use (X.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.shape)) print ("test_set_x_flatten shape: " + str(test_set_x_flatten.shape)) print ("test_set_y shape: " + str(test_set_y.shape))
输出
为了表示彩色图像,必须为每个像素指定红色,绿色和蓝色通道(RGB),因此像素值实际上是从0到255范围内的三个数字的向量。机器学习中一个常见的预处理步骤是对数据集进行居中和标准化,这意味着可以减去每个示例中整个numpy数组的平均值,然后将每个示例除以整个numpy数组的标准偏差。但对于图片数据集,它更简单,更方便,几乎可以将数据集的每一行除以255(像素通道的最大值),因为在RGB中不存在比255大的数据,所以我们可以放心的除以255,让标准化的数据位于[0,1]之间,现在标准化我们的数据集:
train_set_x = train_set_x_flatten / 255. test_set_x = test_set_x_flatten / 255.
对新数据集进行预处理的常见步骤是:
1、找出问题的尺寸和形状(m_train, m_test, num_px,…)
2、重塑数据集,使每个示例现在都是大小为(num_px * num_px * 3,1)的向量。
3、“标准化”的数据
接下来要设计一个简单的算法,使用逻辑回归的神经网络。
数学表达式:
建立神经网络的主要步骤是:
1、定义模型结构(例如输入特征的数量)
2、初始化模型的参数
3、循环:
3.1 计算当前损失(正向传播)
3.2 计算当前梯度(反向传播)
3.3 更新参数(梯度下降)
构建sigmoid()。
# GRADED FUNCTION: sigmoid def sigmoid(z): """ Compute the sigmoid of z Arguments: z -- 任何大小的标量或numpy数组。 Return: s -- sigmoid(z) """ #(≈ 1 line of code) # s = ... # YOUR CODE STARTS HERE s = 1 / (1 + np.exp(-z)) # YOUR CODE ENDS HERE return s
让我们来测试一下sigmoid()函数
print ("sigmoid([0, 2]) = " + str(sigmoid(np.array([0,2])))) #输出分别为sigmoid(0)和sigmoid(2) sigmoid_test(sigmoid)
x = np.array([0.5, 0, 2.0]) output = sigmoid(x) print(output)
实现参数初始化
提示
使用 np.zeros() 。返回来一个给定形状和类型的用0填充的数组。
def initialize_with_zeros(dim): """ 此函数为w创建一个维度为(dim,1)的0向量,并将b初始化为0。 参数: dim - 我们想要的w矢量的大小(或者这种情况下的参数数量) 返回: w - 维度为(dim,1)的初始化向量。 b - 初始化的标量(对应于偏差) """ w = np.zeros(shape = (dim,1)) b = float(0) #注意这个地方一定要定义为浮点型,否则会报错 #使用断言来确保我要的数据是正确的 assert(w.shape == (dim, 1)) #w的维度是(dim,1) assert(isinstance(b, float) or isinstance(b, int)) #b的类型是float或者是int return (w , b)
现在可以执行向前/向后传播步骤来学习参数了。
首先,要实现一个计算成本函数及其渐变的函数propagate()。
提示:
提示
使用 np.log()和 np.dot()【(两个矩阵相乘)】和np.sum()【可以用于计算i的累加】。
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) #计算激活值,请参考公式2。 cost = (- 1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A))) #计算成本,请参考公式3和4。 #反向传播 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 == ()) #创建一个字典,把dw和db保存起来。 grads = { "dw": dw, "db": db } return (grads , cost)
然后进行测试
w = np.array([[1.], [2]]) b = 1.5 X = np.array([[1., -2., -1.], [3., 0.5, -3.2]]) Y = np.array([[1, 1, 0]]) grads, cost = propagate(w, b, X, Y) assert type(grads["dw"]) == np.ndarray assert grads["dw"].shape == (2, 1) assert type(grads["db"]) == np.float64 print ("dw = " + str(grads["dw"])) print ("db = " + str(grads["db"])) print ("cost = " + str(cost)) propagate_test(propagate)
输出为
使用梯度下降来更新参数。
目标是通过最小化成本函数 J 来学习 w 和b 。其中
θ
\theta
θ 的更新规则是
θ
=
θ
−
α
d
(
θ
)
\theta = \theta-\alpha d( \theta )
θ=θ−αd(θ). 其中
α
\alpha
α 是学习率。
# GRADED FUNCTION: optimize def optimize(w, b, X, Y, num_iterations=100, learning_rate=0.009, print_cost=False): """ 此函数通过运行梯度下降算法来优化w和b 参数: w - 权重,大小不等的数组(num_px * num_px * 3,1) b - 偏差,一个标量 X - 维度为(num_px * num_px * 3,训练数据的数量)的数组。 Y - 真正的“标签”矢量(如果非猫则为0,如果是猫则为1),矩阵维度为(1,训练数据的数量) num_iterations - 优化循环的迭代次数 learning_rate - 梯度下降更新规则的学习率 print_cost - 每100步打印一次损失值 返回: params - 包含权重w和偏差b的字典 grads - 包含权重和偏差相对于成本函数的梯度的字典 成本 - 优化期间计算的所有成本列表,将用于绘制学习曲线。 提示: 我们需要写下两个步骤并遍历它们: 1)计算当前参数的成本和梯度,使用propagate()。 2)使用w和b的梯度下降法则更新参数。 """ w = copy.deepcopy(w) b = copy.deepcopy(b) costs = [] for i in range(num_iterations): # (≈ 1 lines of code) # Cost and gradient calculation # grads, cost = ... # YOUR CODE STARTS HERE grads, cost = propagate(w, b, X, Y) # YOUR CODE ENDS HERE # Retrieve derivatives from grads dw = grads["dw"] db = grads["db"] # update rule (≈ 2 lines of code) # w = ... # b = ... # YOUR CODE STARTS HERE w = w - learning_rate* dw b = b - learning_rate * db # YOUR CODE ENDS HERE # Record the costs if i % 100 == 0: costs.append(cost) # Print the cost every 100 training iterations if print_cost: print ("Cost after iteration %i: %f" %(i, 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=False) print ("w = " + str(params["w"])) print ("b = " + str(params["b"])) print ("dw = " + str(grads["dw"])) print ("db = " + str(grads["db"])) print("Costs = " + str(costs)) optimize_test(optimize)
输出
optimize函数会输出已学习的w和b的值,我们可以使用w和b来预测数据集X的标签。现在我们要实现预测函数predict()。计算预测有两个步骤:
# GRADED FUNCTION: predict def predict(w, b, X): ''' 使用学习逻辑回归参数logistic (w,b)预测标签是0还是1, 参数: w - 权重,大小不等的数组(num_px * num_px * 3,1) b - 偏差,一个标量 X - 维度为(num_px * num_px * 3,训练数据的数量)的数据 返回: Y_prediction - 包含X中所有图片的所有预测【0 | 1】的一个numpy数组(向量) ''' m = X.shape[1] #图片的数量 Y_prediction = np.zeros((1, m)) w = w.reshape(X.shape[0], 1) # 计算A,预测猫在图片中出现的概率 #(≈ 1 line of code) # A = ... # YOUR CODE STARTS HERE A = sigmoid(np.dot(w.T , X) + b) # YOUR CODE ENDS HERE for i in range(A.shape[1]): # Convert probabilities A[0,i] to actual predictions p[0,i] #(≈ 4 lines of code) # if A[0, i] > ____ : # Y_prediction[0,i] = # else: # Y_prediction[0,i] = # YOUR CODE STARTS HERE if A[0, i] > 0.5 : Y_prediction[0,i] = 1 else: Y_prediction[0,i] = 0 # YOUR CODE ENDS HERE return Y_prediction
测试
w = np.array([[0.1124579], [0.23106775]]) b = -0.3 X = np.array([[1., -1.1, -3.2],[1.2, 2., 0.1]]) print ("predictions = " + str(predict(w, b, X))) predict_test(predict)
结果
把这些函数统统整合到一个model()函数中。
def model(X_train , Y_train , X_test , Y_test , num_iterations = 2000 , learning_rate = 0.5 , print_cost = False): """ 通过调用之前实现的函数来构建逻辑回归模型 参数: X_train - numpy的数组,维度为(num_px * num_px * 3,m_train)的训练集 Y_train - numpy的数组,维度为(1,m_train)(矢量)的训练标签集 X_test - numpy的数组,维度为(num_px * num_px * 3,m_test)的测试集 Y_test - numpy的数组,维度为(1,m_test)的(向量)的测试标签集 num_iterations - 表示用于优化参数的迭代次数的超参数 learning_rate - 表示optimize()更新规则中使用的学习速率的超参数 print_cost - 设置为true以每100次迭代打印成本 返回: d - 包含有关模型信息的字典。 """ w , b = initialize_with_zeros(X_train.shape[0]) parameters , grads , costs = optimize(w , b , X_train , Y_train,num_iterations , learning_rate , print_cost) #从字典“参数”中检索参数w和b w , b = parameters["w"] , parameters["b"] #预测测试/训练集的例子 Y_prediction_test = predict(w , b, X_test) Y_prediction_train = predict(w , b, X_train) #打印训练后的准确性 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_prediciton_train" : Y_prediction_train, "w" : w, "b" : b, "learning_rate" : learning_rate, "num_iterations" : num_iterations } return d
最后进行测试
logistic_regression_model = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations=2000, learning_rate=0.005, print_cost=True)
结果(迭代的次数:误差值)
图像分类错误的例子
index = 1 plt.imshow(test_set_x[:, index].reshape((num_px, num_px, 3))) print ("y = " + str(test_set_y[0,index]) + ", you predicted that it is a \"" + classes[int(logistic_regression_model['Y_prediction_test'][0,index])].decode("utf-8") + "\" picture.")