精华内容
下载资源
问答
  • 吴恩达课后编程作业】Course 4 - 卷积神经网络 - 第三周作业
    2021-08-10 23:18:34

     

    自己在学习的时候遇到了很多问题,参考了很多大神的代码,发现网上其他的例子大多用的旧版的tensorflow和keras,由于tensorflow与keras都存在新旧版不兼容的情况,代码不能直接拿来用,这里提供一个python=3.8;tensorflow=2.3.0;keras=2.4.3环境下可以运行的代码

    过程中遇到的报错:

     “No training configuration found in save file:the model was *not* compiled. Compile it manually”

    tensorflow.python.framework.errors_impl.FailedPreconditionError: Error while reading resource variable conv2d_10/kernel from Container: localhost. This could mean that the variable was uninitialized. Not found: Container localhost does not exist. (Could not find resource: localhost/conv2d_10/kernel)

    'numpy.dtype[float32]' object has no attribute 'base_dtype'

    参考

    https://blog.csdn.net/u013733326/article/details/80341740

    https://blog.csdn.net/Solo95/article/details/85262828

    https://github.com/Dragon-GCS/Python/blob/master/%E5%90%B4%E6%81%A9%E8%BE%BE%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E8%AF%BE%E5%90%8E%E4%BD%9C%E4%B8%9A/%E7%AC%AC%E5%9B%9B%E5%91%A8/3_vehicle_identification.py

    https://stackoverflow.com/questions/57555407/numpy-dtype-object-has-no-attribute-base-dtype-in-keras

     

    import argparse
    import os
    import matplotlib.pyplot as plt
    from matplotlib.pyplot import imshow
    import scipy.io
    import scipy.misc
    import numpy as np
    import pandas as pd
    import PIL
    import tensorflow as tf
    from tensorflow import keras
    import keras.backend as K
    from keras.layers import Input, Lambda, Conv2D
    from keras.models import load_model, Model
    from yad2k.models.keras_yolo import yolo_head, yolo_boxes_to_corners, preprocess_true_boxes, yolo_loss, yolo_body
    import yolo_utils
    tf.compat.v1.disable_eager_execution()
    
    
    def yolo_filter_boxes(box_confidence, boxes, box_class_probs, threshold=0.6):
        """
            通过阈值来过滤对象和分类的置信度。
    
            参数:
                box_confidence  - tensor类型,维度为(19,19,5,1),包含19x19单元格中每个单元格预测的5个锚框中的所有的锚框的pc (一些对象的置信概率)。
                boxes - tensor类型,维度为(19,19,5,4),包含了所有的锚框的(px,py,ph,pw )。
                box_class_probs - tensor类型,维度为(19,19,5,80),包含了所有单元格中所有锚框的所有对象( c1,c2,c3,···,c80 )检测的概率。
                threshold - 实数,阈值,如果分类预测的概率高于它,那么这个分类预测的概率就会被保留。
    
            返回:
                scores - tensor 类型,维度为(None,),包含了保留了的锚框的分类概率。
                boxes - tensor 类型,维度为(None,4),包含了保留了的锚框的(b_x, b_y, b_h, b_w)
                classess - tensor 类型,维度为(None,),包含了保留了的锚框的索引
    
            注意:"None"是因为你不知道所选框的确切数量,因为它取决于阈值。
                  比如:如果有10个锚框,scores的实际输出大小将是(10,)
        """
    
        # 第一步:计算锚框的得分
        box_scores = box_confidence * box_class_probs
    
        # 第二步:找到最大值的锚框的索引以及对应的最大值的锚框的分数
        box_classes = K.argmax(box_scores, axis=-1)
        box_class_scores = K.max(box_scores, axis=-1)
    
        # 第三步:根据阈值创建掩码
        filtering_mask = (box_class_scores >= threshold)
    
        # 对scores, boxes 以及 classes使用掩码
        scores = tf.boolean_mask(box_class_scores, filtering_mask)
        boxes = tf.boolean_mask(boxes, filtering_mask)
        classes = tf.boolean_mask(box_classes, filtering_mask)
    
        return scores, boxes, classes
    
    
    def iou(box1, box2):
        """
            实现两个锚框的交并比的计算
    
            参数:
                box1 - 第一个锚框,元组类型,(x1, y1, x2, y2)
                box2 - 第二个锚框,元组类型,(x1, y1, x2, y2)
    
            返回:
                iou - 实数,交并比。
        """
        # 计算相交的区域的面积
        xi1 = np.maximum(box1[0], box2[0])
        yi1 = np.maximum(box1[1], box2[1])
        xi2 = np.minimum(box1[2], box2[2])
        yi2 = np.minimum(box1[3], box2[3])
        inter_area = (xi1-xi2)*(yi1-yi2)
    
        # 计算并集,公式为:Union(A,B) = A + B - Inter(A,B)
        box1_area = (box1[0]-box1[2])*(box1[1]-box1[3])
        box2_area = (box2[0]-box2[2])*(box2[1]-box2[3])
        union_area = box1_area + box2_area - inter_area
    
        # 计算交并比
        iou = inter_area / union_area
        return iou
    
    
    def yolo_non_max_suppression(scores, boxes, classes, max_boxes=10, iou_threshold=0.5):
        """
            为锚框实现非最大值抑制( Non-max suppression (NMS))
    
            参数:
                scores - tensor类型,维度为(None,),yolo_filter_boxes()的输出
                boxes - tensor类型,维度为(None,4),yolo_filter_boxes()的输出,已缩放到图像大小(见下文)
                classes - tensor类型,维度为(None,),yolo_filter_boxes()的输出
                max_boxes - 整数,预测的锚框数量的最大值
                iou_threshold - 实数,交并比阈值。
    
            返回:
                scores - tensor类型,维度为(,None),每个锚框的预测的可能值
                boxes - tensor类型,维度为(4,None),预测的锚框的坐标
                classes - tensor类型,维度为(,None),每个锚框的预测的分类
    
            注意:"None"是明显小于max_boxes的,这个函数也会改变scores、boxes、classes的维度,这会为下一步操作提供方便。
    
        """
        max_boxes_tensor = K.variable(max_boxes, dtype="int32")#用于tf.image.non_max_suppression()
        tf.compat.v1.Session().run(tf.compat.v1.variables_initializer([max_boxes_tensor]))#初始化变量max_boxes_tensor
    
        # 使用使用tf.image.non_max_suppression()来获取与我们保留的框相对应的索引列表
        nms_indices = tf.compat.v1.image.non_max_suppression(boxes, scores, max_boxes, iou_threshold)
    
        # 使用K.gather()来选择保留的锚框
        scores = K.gather(scores, nms_indices)
        boxes = K.gather(boxes, nms_indices)
        classes = K.gather(classes, nms_indices)
        return scores, boxes, classes
    
    
    def yolo_eval(yolo_outputs, image_shape=(720., 1280.), max_boxes=10, score_threshold=0.6, iou_threshold=0.5):
        """
            将YOLO编码的输出(很多锚框)转换为预测框以及它们的分数,框坐标和类。
    
            参数:
                yolo_outputs - 编码模型的输出(对于维度为(608,608,3)的图片),包含4个tensors类型的变量:
                                box_confidence : tensor类型,维度为(None, 19, 19, 5, 1)
                                box_xy         : tensor类型,维度为(None, 19, 19, 5, 2)
                                box_wh         : tensor类型,维度为(None, 19, 19, 5, 2)
                                box_class_probs: tensor类型,维度为(None, 19, 19, 5, 80)
                image_shape - tensor类型,维度为(2,),包含了输入的图像的维度,这里是(608.,608.)
                max_boxes - 整数,预测的锚框数量的最大值
                score_threshold - 实数,可能性阈值。
                iou_threshold - 实数,交并比阈值。
    
            返回:
                scores - tensor类型,维度为(,None),每个锚框的预测的可能值
                boxes - tensor类型,维度为(4,None),预测的锚框的坐标
                classes - tensor类型,维度为(,None),每个锚框的预测的分类
        """
    
        # 获取YOLO模型的输出
        box_confidence, box_xy, box_wh, box_class_probs = yolo_outputs
    
        # 中心点转换为边角
        boxes = yolo_boxes_to_corners(box_xy, box_wh)
    
        # 可信度分值过滤
        scores, boxes, classes = yolo_filter_boxes(box_confidence, boxes, box_class_probs, score_threshold)
    
        # 缩放锚框,以适应原始图像
        boxes = yolo_utils.scale_boxes(boxes, image_shape)
    
        # 使用非最大值抑制
        scores, boxes, classes = yolo_non_max_suppression(scores, boxes, classes, max_boxes, iou_threshold)
        return scores, boxes, classes
    
    
    def predict(model, image_file, is_show_info=True, is_plot=True):
        """
            运行存储在sess的计算图以预测image_file的边界框,打印出预测的图与信息。
            和网上多数代码区别最大的地方就在这一段,1.把模型的加载移到了函数外部,避免多次加载产生报错;2.对输入数据类型进行了调整
    
            参数:
                model - 包含了YOLO计算图的TensorFlow/Keras的会话。
                image_file - 存储在images文件夹下的图片名称
            返回:
                scores - tensor类型,维度为(None,),锚框的预测的可能值。
                boxes - tensor类型,维度为(None,4),包含了锚框位置信息。
                classes - tensor类型,维度为(None,),锚框的预测的分类索引。
        """
        # 图像预处理
        class_names = yolo_utils.read_classes("model_data/coco_classes.txt")
        anchors = yolo_utils.read_anchors("model_data/yolo_anchors.txt")
        image_shape = (720., 1280.)
        image, image_data = yolo_utils.preprocess_image("images/" + image_file, model_image_size=(608, 608))
    
        # 预测图像,结果为(1,19,19,425)最后的维度为5个锚框x85个属性
        yolo_model_output = model.predict(image_data)
    
        # 直接将yolo_model_output输入yolo_head会导致报错,需要用K.constant()对数据类型进行修改
        yolo_outputs = yolo_head(K.constant(yolo_model_output), anchors, len(class_names))
        scores, boxes, classes = yolo_eval(yolo_outputs, image_shape)
    
        # 打印预测信息
        if is_show_info:
            print("在" + str(image_file) + "中找到了" + str(len(K.get_value(boxes))) + "个锚框。")
    
        # 指定要绘制的边界框的颜色
        colors = yolo_utils.generate_colors(class_names)
    
        # 在图中绘制边界框
        yolo_utils.draw_boxes(image, K.get_value(scores), K.get_value(boxes), K.get_value(classes), class_names, colors)#用K.get_value()规范输入数据的类型
    
        # 保存已经绘制了边界框的图
        image.save(os.path.join("out", image_file), quality=100)
    
        # 打印出已经绘制了边界框的图
        if is_plot:
            output_image = plt.imread(os.path.join("out", image_file))
            plt.imshow(output_image)
            plt.show()
    
        return scores, boxes, classes
    
    
    yolo_model = keras.models.load_model("model_data/yolo.h5")
    predict(yolo_model, "test.jpg")
    
    
    
    for i in range(1, 121):
        # 计算需要在前面填充几个0
        num_fill = int(len("0000") - len(str(1))) + 1
    
        # 对索引进行填充
        filename = str(i).zfill(num_fill) + ".jpg"
    
        # 开始绘制,不打印信息,不绘制图
        print("当前文件: " + str(filename))
        predict(yolo_model, filename, is_show_info=False, is_plot=False)
    
    print("绘制完成!")
    
    更多相关内容
  • YOLO,吴恩达课后编程作业,Course 4 - 卷积,训练model_data/yolov2.h5的项目,直接下载,查看我发的另一篇博客关于这个资源的使用
  • 该资源是吴恩达公开课后的8次作业的习题+编程答案,编程答案分为python版本和matlab版本
  • 吴恩达课后编程作业】Course 2 - 改善深层神经网络 - 第三周作业 - TensorFlow入门.zip
  • 吴恩达课后编程作业】Course 2 - 改善深层神经网络 - 第一周作业(1&2&3).zip
  • 吴恩达课后编程作业】Course 1 - 神经网络和深度学习 - 第二周作业 - 具有神经网络思维的Logistic回归 上一篇:【课程1 - 第二周测验】※※※※※ 【回到目录】※※※※※下一篇:【课程1 - 第三周测验】 ...

    【吴恩达课后编程作业】Course 1 - 神经网络和深度学习 - 第二周作业 - 具有神经网络思维的Logistic回归


    上一篇: 【课程1 - 第二周测验】※※※※※ 【回到目录】※※※※※下一篇: 【课程1 - 第三周测验】

    在开始之前,首先声明本文参考【Kulbear】的github上的文章,本文参考Logistic Regression with a Neural Network mindset,我基于他的文章加以自己的理解发表这篇博客,力求让大家以最轻松的姿态理解吴恩达的视频,如有不妥的地方欢迎大家指正。


    本文所使用的资料已上传到百度网盘【点击下载】,提取码:2u3w ,请在开始之前下载好所需资料,然后将文件解压到你的代码文件同一级目录下,请确保你的代码那里有lr_utils.py和datasets文件夹。


    【博主使用的python版本:3.6.2】


    我们要做的事是搭建一个能够**【识别猫】** 的简单的神经网络,你可以跟随我的步骤在Jupyter Notebook中一步步地把代码填进去,也可以直接复制完整代码,在完整代码在本文最底部。

    在开始之前,我们有需要引入的库:

    • numpy :是用Python进行科学计算的基本软件包。
    • h5py:是与H5文件中存储的数据集进行交互的常用软件包。
    • matplotlib:是一个著名的库,用于在Python中绘制图表。
    • lr_utils :在本文的资料包里,一个加载资料包里面的数据的简单功能的库。

    如果你没有以上的库,请自行安装。

    import numpy as np
    import matplotlib.pyplot as plt
    import h5py
    from lr_utils import load_dataset
    
    

    lr_utils.py代码如下,你也可以自行打开它查看:

    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
    

    解释以下上面的load_dataset() 返回的值的含义:

    • 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’]。

    现在我们就要把这些数据加载到主程序里面:

    train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes = load_dataset()
    

    我们可以看一下我们加载的文件里面的图片都是些什么样子的,比如我就查看一下训练集里面的第26张图片,当然你也可以改变index的值查看一下其他的图片。

    index = 25
    plt.imshow(train_set_x_orig[index])
    #print("train_set_y=" + str(train_set_y)) #你也可以看一下训练集里面的标签是什么样的。
    

    运行结果如下:
    index_25

    现在我们可以结合一下训练集里面的数据来看一下我到底都加载了一些什么东西。

    #打印出当前的训练标签值
    #使用np.squeeze的目的是压缩维度,【未压缩】train_set_y[:,index]的值为[1] , 【压缩后】np.squeeze(train_set_y[:,index])的值为1
    #print("【使用np.squeeze:" + str(np.squeeze(train_set_y[:,index])) + ",不使用np.squeeze: " + str(train_set_y[:,index]) + "】")
    #只有压缩后的值才能进行解码操作
    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)。

    请记住,train_set_x_orig 是一个维度为(m_​​train,num_px,num_px,3)的数组。

    m_train = train_set_y.shape[1] #训练集里图片的数量。
    m_test = test_set_y.shape[1] #测试集里图片的数量。
    num_px = train_set_x_orig.shape[1] #训练、测试集里面的图片的宽度和高度(均为64x64)。
    
    #现在看一看我们加载的东西的具体情况
    print ("训练集的数量: m_train = " + str(m_train))
    print ("测试集的数量 : m_test = " + str(m_test))
    print ("每张图片的宽/高 : num_px = " + str(num_px))
    print ("每张图片的大小 : (" + str(num_px) + ", " + str(num_px) + ", 3)")
    print ("训练集_图片的维数 : " + str(train_set_x_orig.shape))
    print ("训练集_标签的维数 : " + str(train_set_y.shape))
    print ("测试集_图片的维数: " + str(test_set_x_orig.shape))
    print ("测试集_标签的维数: " + str(test_set_y.shape))
    

    运行之后的结果:

    训练集的数量: m_train = 209
    测试集的数量 : m_test = 50
    每张图片的宽/: num_px = 64
    每张图片的大小 : (64, 64, 3)
    训练集_图片的维数 : (209, 64, 64, 3)
    训练集_标签的维数 : (1, 209)
    测试集_图片的维数: (50, 64, 64, 3)
    测试集_标签的维数: (1, 50)
    

    为了方便,我们要把维度为(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时,可以使用以下代码:

    #X_flatten = X.reshape(X.shape [0],-1).T #X.T是X的转置
    #将训练集的维度降低并转置。
    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
    

    这一段意思是指把数组变为209行的矩阵(因为训练集里有209张图片),但是我懒得算列有多少,于是我就用-1告诉程序你帮我算,最后程序算出来时12288列,我再最后用一个T表示转置,这就变成了12288行,209列。测试集亦如此。

    然后我们看看降维之后的情况是怎么样的:

    print ("训练集降维最后的维度: " + str(train_set_x_flatten.shape))
    print ("训练集_标签的维数 : " + str(train_set_y.shape))
    print ("测试集降维之后的维度: " + str(test_set_x_flatten.shape))
    print ("测试集_标签的维数 : " + str(test_set_y.shape))
    

    执行之后的结果为:

    训练集降维最后的维度: (12288, 209)
    训练集_标签的维数 : (1, 209)
    测试集降维之后的维度: (12288, 50)
    测试集_标签的维数 : (1, 50)
    

    为了表示彩色图像,必须为每个像素指定红色,绿色和蓝色通道(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
    

    flatten


    现在总算是把我们加载的数据弄完了,我们现在开始构建神经网络。

    以下是数学表达式,如果对数学公式不甚理解,请仔细看一下吴恩达的视频。

    对于 x ( i ) x^{(i)} x(i): z ( i ) = w T x ( i ) + b (1) z^{(i)} = w^T x^{(i)} + b \tag{1} z(i)=wTx(i)+b(1) y ^ ( i ) = a ( i ) = s i g m o i d ( z ( i ) ) (2) \hat{y}^{(i)} = a^{(i)} = sigmoid(z^{(i)})\tag{2} y^(i)=a(i)=sigmoid(z(i))(2) L ( a ( i ) , y ( i ) ) = − y ( i ) log ⁡ ( a ( i ) ) − ( 1 − y ( i ) ) log ⁡ ( 1 − a ( i ) ) (3) \mathcal{L}(a^{(i)}, y^{(i)}) = - y^{(i)} \log(a^{(i)}) - (1-y^{(i)} ) \log(1-a^{(i)})\tag{3} L(a(i),y(i))=y(i)log(a(i))(1y(i))log(1a(i))(3)

    然后通过对所有训练样例求和来计算成本: J = 1 m ∑ i = 1 m L ( a ( i ) , y ( i ) ) (4) J = \frac{1}{m} \sum_{i=1}^m \mathcal{L}(a^{(i)}, y^{(i)})\tag{4} J=m1i=1mL(a(i),y(i))(4)

    建立神经网络的主要步骤是:

    1. 定义模型结构(例如输入特征的数量)

    2. 初始化模型的参数

    3. 循环:

      3.1 计算当前损失(正向传播)

      3.2 计算当前梯度(反向传播)

      3.3 更新参数(梯度下降)

    现在构建sigmoid(),需要使用 sigmoid(w ^ T x + b) 计算来做出预测。

    def sigmoid(z):
        """
        参数:
            z  - 任何大小的标量或numpy数组。
        
        返回:
            s  -  sigmoid(z)
        """
        s = 1 / (1 + np.exp(-z))
        return s
    

    我们可以测试一下sigmoid(),检查一下是否符合我们所需要的条件。

    #测试sigmoid()
    print("====================测试sigmoid====================")
    print ("sigmoid(0) = " + str(sigmoid(0)))
    print ("sigmoid(9.2) = " + str(sigmoid(9.2)))
    

    打印出的结果为:

    ====================测试sigmoid====================
    sigmoid(0) = 0.5
    sigmoid(9.2) = 0.999898970806
    

    既然sigmoid测试好了,我们现在就可以初始化我们需要的参数w和b了。

    def initialize_with_zeros(dim):
        """
            此函数为w创建一个维度为(dim,1)的0向量,并将b初始化为0。
            
            参数:
                dim  - 我们想要的w矢量的大小(或者这种情况下的参数数量)
            
            返回:
                w  - 维度为(dim,1)的初始化向量。
                b  - 初始化的标量(对应于偏差)
        """
        w = np.zeros(shape = (dim,1))
        b = 0
        #使用断言来确保我要的数据是正确的
        assert(w.shape == (dim, 1)) #w的维度是(dim,1)
        assert(isinstance(b, float) or isinstance(b, int)) #b的类型是float或者是int
        
        return (w , b)
    

    初始化参数的函数已经构建好了,现在就可以执行“前向”和“后向”传播步骤来学习参数。

    我们现在要实现一个计算成本函数及其渐变的函数propagate()。

    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)
    

    写好之后我们来测试一下。

    #测试一下propagate
    print("====================测试propagate====================")
    #初始化一些参数
    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))
    

    测试结果是:

    ====================测试propagate====================
    dw = [[ 0.99993216]
     [ 1.99980262]]
    db = 0.499935230625
    cost = 6.00006477319
    

    现在,我要使用渐变下降更新参数。

    目标是通过最小化成本函数 J J J 来学习 w w w b b b 。对于参数 θ \theta θ ,更新规则是 $ \theta = \theta - \alpha \text{ } d\theta$,其中 α \alpha α 是学习率。

    def optimize(w , b , X , Y , num_iterations , learning_rate , 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的梯度下降法则更新参数。
        """
        
        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,cost))
            
        params  = {
                    "w" : w,
                    "b" : b }
        grads = {
                "dw": dw,
                "db": db } 
        return (params , grads , costs)
    

    现在就让我们来测试一下优化函数:

    #测试optimize
    print("====================测试optimize====================")
    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 , 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"]))
    

    测试结果为:

    ====================测试optimize====================
    w = [[ 0.1124579 ]
     [ 0.23106775]]
    b = 1.55930492484
    dw = [[ 0.90158428]
     [ 1.76250842]]
    db = 0.430462071679
    

    optimize函数会输出已学习的w和b的值,我们可以使用w和b来预测数据集X的标签。

    现在我们要实现预测函数predict()。计算预测有两个步骤:

    1. 计算 Y ^ = A = σ ( w T X + b ) \hat{Y} = A = \sigma(w^T X + b) Y^=A=σ(wTX+b)

    2. 将a的值变为0(如果激活值<= 0.5)或者为1(如果激活值> 0.5),

    然后将预测值存储在向量Y_prediction中。

    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 = sigmoid(np.dot(w.T , X) + b)
        for i in range(A.shape[1]):
            #将概率a [0,i]转换为实际预测p [0,i]
            Y_prediction[0,i] = 1 if A[0,i] > 0.5 else 0
        #使用断言
        assert(Y_prediction.shape == (1,m))
        
        return Y_prediction
    

    老规矩,测试一下。

    #测试predict
    print("====================测试predict====================")
    w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])
    print("predictions = " + str(predict(w, b, X)))
    

    就目前而言,我们基本上把所有的东西都做完了,现在我们要把这些函数统统整合到一个model()函数中,届时只需要调用一个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
    

    把整个model构建好之后我们这就算是正式的实际测试了,我们这就来实际跑一下。

    print("====================测试model====================")     
    #这里加载的是真实的数据,请参见上面的代码部分。
    d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)
    

    执行后的数据:

    ====================测试model====================
    迭代的次数: 0 , 误差值: 0.693147
    迭代的次数: 100 , 误差值: 0.584508
    迭代的次数: 200 , 误差值: 0.466949
    迭代的次数: 300 , 误差值: 0.376007
    迭代的次数: 400 , 误差值: 0.331463
    迭代的次数: 500 , 误差值: 0.303273
    迭代的次数: 600 , 误差值: 0.279880
    迭代的次数: 700 , 误差值: 0.260042
    迭代的次数: 800 , 误差值: 0.242941
    迭代的次数: 900 , 误差值: 0.228004
    迭代的次数: 1000 , 误差值: 0.214820
    迭代的次数: 1100 , 误差值: 0.203078
    迭代的次数: 1200 , 误差值: 0.192544
    迭代的次数: 1300 , 误差值: 0.183033
    迭代的次数: 1400 , 误差值: 0.174399
    迭代的次数: 1500 , 误差值: 0.166521
    迭代的次数: 1600 , 误差值: 0.159305
    迭代的次数: 1700 , 误差值: 0.152667
    迭代的次数: 1800 , 误差值: 0.146542
    迭代的次数: 1900 , 误差值: 0.140872
    训练集准确性: 99.04306220095694 %
    测试集准确性: 70.0 %
    

    我们更改一下学习率和迭代次数,有可能会发现训练集的准确性可能会提高,但是测试集准确性会下降,这是由于过拟合造成的,但是我们并不需要担心,我们以后会使用更好的算法来解决这些问题的。


    到目前为止,我们的程序算是完成了,但是,我们可以在后面加一点东西,比如画点图什么的。

    #绘制图
    costs = np.squeeze(d['costs'])
    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('iterations (per hundreds)')
    plt.title("Learning rate =" + str(d["learning_rate"]))
    plt.show()
    

    跑一波出来的效果图是这样的,可以看到成本下降,它显示参数正在被学习:
    pic

    让我们进一步分析一下,并研究学习率alpha的可能选择。为了让渐变下降起作用,我们必须明智地选择学习速率。学习率 α \alpha α 决定了我们更新参数的速度。如果学习率过高,我们可能会“超过”最优值。同样,如果它太小,我们将需要太多迭代才能收敛到最佳值。这就是为什么使用良好调整的学习率至关重要的原因。

    我们可以比较一下我们模型的学习曲线和几种学习速率的选择。也可以尝试使用不同于我们初始化的learning_rates变量包含的三个值,并看一下会发生什么。

    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()
    

    跑一下打印出来的结果是:

    learning rate is: 0.01
    训练集准确性: 99.52153110047847 %
    测试集准确性: 68.0 %
    
    -------------------------------------------------------
    
    learning rate is: 0.001
    训练集准确性: 88.99521531100478 %
    测试集准确性: 64.0 %
    
    -------------------------------------------------------
    
    learning rate is: 0.0001
    训练集准确性: 68.42105263157895 %
    测试集准确性: 36.0 %
    
    -------------------------------------------------------
    

    pic2


    **【完整代码】: **

    # -*- coding: utf-8 -*-
    """
    Created on Wed Mar 21 17:25:30 2018
    
    博客地址 :http://blog.csdn.net/u013733326/article/details/79639509
    
    @author: Oscar
    """
    
    import numpy as np
    import matplotlib.pyplot as plt
    import h5py
    from lr_utils import load_dataset
    
    train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes = load_dataset()
    
    m_train = train_set_y.shape[1] #训练集里图片的数量。
    m_test = test_set_y.shape[1] #测试集里图片的数量。
    num_px = train_set_x_orig.shape[1] #训练、测试集里面的图片的宽度和高度(均为64x64)。
    
    #现在看一看我们加载的东西的具体情况
    print ("训练集的数量: m_train = " + str(m_train))
    print ("测试集的数量 : m_test = " + str(m_test))
    print ("每张图片的宽/高 : num_px = " + str(num_px))
    print ("每张图片的大小 : (" + str(num_px) + ", " + str(num_px) + ", 3)")
    print ("训练集_图片的维数 : " + str(train_set_x_orig.shape))
    print ("训练集_标签的维数 : " + str(train_set_y.shape))
    print ("测试集_图片的维数: " + str(test_set_x_orig.shape))
    print ("测试集_标签的维数: " + str(test_set_y.shape))
    
    #将训练集的维度降低并转置。
    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 ("训练集降维最后的维度: " + str(train_set_x_flatten.shape))
    print ("训练集_标签的维数 : " + str(train_set_y.shape))
    print ("测试集降维之后的维度: " + str(test_set_x_flatten.shape))
    print ("测试集_标签的维数 : " + str(test_set_y.shape))
    
    train_set_x = train_set_x_flatten / 255
    test_set_x = test_set_x_flatten / 255
    
    def sigmoid(z):
        """
        参数:
            z  - 任何大小的标量或numpy数组。
    
        返回:
            s  -  sigmoid(z)
        """
        s = 1 / (1 + np.exp(-z))
        return s
    
    def initialize_with_zeros(dim):
        """
            此函数为w创建一个维度为(dim,1)的0向量,并将b初始化为0。
    
            参数:
                dim  - 我们想要的w矢量的大小(或者这种情况下的参数数量)
    
            返回:
                w  - 维度为(dim,1)的初始化向量。
                b  - 初始化的标量(对应于偏差)
        """
        w = np.zeros(shape = (dim,1))
        b = 0
        #使用断言来确保我要的数据是正确的
        assert(w.shape == (dim, 1)) #w的维度是(dim,1)
        assert(isinstance(b, float) or isinstance(b, int)) #b的类型是float或者是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) #计算激活值,请参考公式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)
    
    def optimize(w , b , X , Y , num_iterations , learning_rate , 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的梯度下降法则更新参数。
        """
    
        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,cost))
    
        params  = {
                    "w" : w,
                    "b" : b }
        grads = {
                "dw": dw,
                "db": db } 
        return (params , grads , costs)
    
    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 = sigmoid(np.dot(w.T , X) + b)
        for i in range(A.shape[1]):
            #将概率a [0,i]转换为实际预测p [0,i]
            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):
        """
        通过调用之前实现的函数来构建逻辑回归模型
    
        参数:
            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
    
    d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)
    
    #绘制图
    costs = np.squeeze(d['costs'])
    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('iterations (per hundreds)')
    plt.title("Learning rate =" + str(d["learning_rate"]))
    plt.show()
    
    展开全文
  • 吴恩达课后编程作业】Course+1+-+神经网络和深度学习+-+第四周作业(1&2).zip
  • 吴恩达深度学习deeplearning第五课第一周课后测验及编程作业(含答案) 吴恩达深度学习(deeplearning.ai)一到五课全部课后编程题(内有网盘链接)
  • 【中文】【吴恩达课后编程作业】Course 5 - 序列模型 - 第三周作业 - 机器翻译与触发词检测
  • 吴恩达课后编程作业】Course 1 - 神经网络和深度学习 - 第三周作业 - 带有一个隐藏层的平面数据分类 上一篇:【 课程1 - 第三周测验】※※※※※ 【回到目录】※※※※※下一篇:【课程1 - 第四周测验】 ...

    #【吴恩达课后编程作业】Course 1 - 神经网络和深度学习 - 第三周作业 - 带有一个隐藏层的平面数据分类


    上一篇: 【 课程1 - 第三周测验】※※※※※ 【回到目录】※※※※※下一篇: 【课程1 - 第四周测验】

    声明

       首先声明本文参考【Kulbear】的github上的文章,本文参考Planar data classification with one hidden layer,我基于他的文章加以自己的理解发表这篇博客,力求让大家以最轻松的姿态理解吴恩达的视频,如有不妥的地方欢迎大家指正。


    本文所使用的资料已上传到百度网盘**【点击下载】**,提取码:qifu,请在开始之前下载好所需资料,或者在本文底部copy资料代码。


    【博主使用的python版本:3.6.2】


    开始之前

       在开始之前,我们简单说一下我们要做什么。我们要建立一个神经网络,它有一个隐藏层。你会发现这个模型和上一个逻辑回归实现的模型有很大的区别。你可以跟随我的步骤在Jupyter Notebook中一步步地把代码填进去,也可以直接复制完整代码,在完整代码在本文底部,testCases.py和planar_utils.py的完整代码也在最底部。在这篇文章中,我们会讲到以下的知识:

    • 构建具有单隐藏层的2类分类神经网络。
    • 使用具有非线性激活功能激活函数,例如tanh。
    • 计算交叉熵损失(损失函数)。
    • 实现向前和向后传播。

    准备软件包

    我们需要准备一些软件包:

    • numpy:是用Python进行科学计算的基本软件包。
    • sklearn:为数据挖掘和数据分析提供的简单高效的工具。
    • matplotlib :是一个用于在Python中绘制图表的库。
    • testCases:提供了一些测试示例来评估函数的正确性,参见下载的资料或者在底部查看它的代码。
    • planar_utils :提供了在这个任务中使用的各种有用的功能,参见下载的资料或者在底部查看它的代码。
    import numpy as np
    import matplotlib.pyplot as plt
    from testCases import *
    import sklearn
    import sklearn.datasets
    import sklearn.linear_model
    from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
    
    #%matplotlib inline #如果你使用用的是Jupyter Notebook的话请取消注释。
    
    np.random.seed(1) #设置一个固定的随机种子,以保证接下来的步骤中我们的结果是一致的。
    
    

    加载和查看数据集

    首先,我们来看看我们将要使用的数据集, 下面的代码会将一个花的图案的2类数据集加载到变量X和Y中。

    X, Y = load_planar_dataset()
    

      把数据集加载完成了,然后使用matplotlib可视化数据集,代码如下:

    plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral) #绘制散点图
    
    # 上一语句如出现问题,请使用下面的语句:
    plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral) #绘制散点图
    

    flower
       数据看起来像一朵红色(y = 0)和一些蓝色(y = 1)的数据点的花朵的图案。 我们的目标是建立一个模型来适应这些数据。现在,我们已经有了以下的东西:

    • X:一个numpy的矩阵,包含了这些数据点的数值
    • Y:一个numpy的向量,对应着的是X的标签【0 | 1】(红色:0 , 蓝色 :1)

    我们继续来仔细地看数据:

    shape_X = X.shape
    shape_Y = Y.shape
    m = Y.shape[1]  # 训练集里面的数量
    
    print ("X的维度为: " + str(shape_X))
    print ("Y的维度为: " + str(shape_Y))
    print ("数据集里面的数据有:" + str(m) + " 个")
    

    运行结果为:

    X的维度为: (2, 400)
    Y的维度为: (1, 400)
    数据集里面的数据有:400

    查看简单的Logistic回归的分类效果

      在构建完整的神经网络之前,先让我们看看逻辑回归在这个问题上的表现如何,我们可以使用sklearn的内置函数来做到这一点, 运行下面的代码来训练数据集上的逻辑回归分类器。

    clf = sklearn.linear_model.LogisticRegressionCV()
    clf.fit(X.T,Y.T)
    

      这里会打印出以下的信息(不同的机器提示大同小异):
      E:\Anaconda3\lib\site-packages\sklearn\utils\validation.py:547: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples, ), for example using ravel().
      y = column_or_1d(y, warn=True)

    我们可以把逻辑回归分类器的分类绘制出来:

    plot_decision_boundary(lambda x: clf.predict(x), X, Y) #绘制决策边界
    plt.title("Logistic Regression") #图标题
    LR_predictions  = clf.predict(X.T) #预测结果
    print ("逻辑回归的准确性: %d " % float((np.dot(Y, LR_predictions) + 
    		np.dot(1 - Y,1 - LR_predictions)) / float(Y.size) * 100) +
           "% " + "(正确标记的数据点所占的百分比)")
    

    我们看一看都打印了些什么吧!

    逻辑回归的准确性: 47 % (正确标记的数据点所占的百分比)
    

    Logistic Regression
    准确性只有47%的原因是数据集不是线性可分的,所以逻辑回归表现不佳,现在我们正式开始构建神经网络。


    搭建神经网络

    我们要搭建的神经网络模型如下图:
    Neural Network model image
    当然还有我们的理论基础(不懂可以去仔细看看视频):
    对于 x ( i ) x^{(i)} x(i) 而言:
    z [ 1 ] ( i ) = W [ 1 ] x ( i ) + b [ 1 ] ( i ) (1) z^{[1] (i)} = W^{[1]} x^{(i)} + b^{[1] (i)}\tag{1} z[1](i)=W[1]x(i)+b[1](i)(1)
    a [ 1 ] ( i ) = tanh ⁡ ( z [ 1 ] ( i ) ) (2) a^{[1] (i)} = \tanh(z^{[1] (i)})\tag{2} a[1](i)=tanh(z[1](i))(2)
    z [ 2 ] ( i ) = W [ 2 ] a [ 1 ] ( i ) + b [ 2 ] ( i ) (3) z^{[2] (i)} = W^{[2]} a^{[1] (i)} + b^{[2] (i)}\tag{3} z[2](i)=W[2]a[1](i)+b[2](i)(3)
    y ^ ( i ) = a [ 2 ] ( i ) = σ ( z [ 2 ] ( i ) ) (4) \hat{y}^{(i)} = a^{[2] (i)} = \sigma(z^{ [2] (i)})\tag{4} y^(i)=a[2](i)=σ(z[2](i))(4)
    KaTeX parse error: Undefined control sequence: \mbox at position 42: …gin{cases} 1 & \̲m̲b̲o̲x̲{if } a^{[2](i)…
    给出所有示例的预测结果,可以按如下方式计算成本J:
    J = − 1 m ∑ i = 0 m ( y ( i ) log ⁡ ( a [ 2 ] ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − a [ 2 ] ( i ) ) ) (6) J = - \frac{1}{m} \sum\limits_{i = 0}^{m} \large\left(\small y^{(i)}\log\left(a^{[2] (i)}\right) + (1-y^{(i)})\log\left(1- a^{[2] (i)}\right) \large \right) \small \tag{6} J=m1i=0m(y(i)log(a[2](i))+(1y(i))log(1a[2](i)))(6)

    构建神经网络的一般方法是:

    1. 定义神经网络结构(输入单元的数量,隐藏单元的数量等)。
    2. 初始化模型的参数
    3. 循环
    • 实施前向传播
    • 计算损失
    • 实现向后传播
    • 更新参数(梯度下降)

      我们要它们合并到一个nn_model() 函数中,当我们构建好了nn_model()并学习了正确的参数,我们就可以预测新的数据。

    定义神经网络结构

    在构建之前,我们要先把神经网络的结构给定义好:

    • n_x: 输入层的数量
    • n_h: 隐藏层的数量(这里设置为4)
    • n_y: 输出层的数量
    def layer_sizes(X , Y):
        """
        参数:
         X - 输入数据集,维度为(输入的数量,训练/测试的数量)
         Y - 标签,维度为(输出的数量,训练/测试数量)
        
        返回:
         n_x - 输入层的数量
         n_h - 隐藏层的数量
         n_y - 输出层的数量
        """
        n_x = X.shape[0] #输入层
        n_h = 4 #,隐藏层,硬编码为4
        n_y = Y.shape[0] #输出层
        
        return (n_x,n_h,n_y)
    

    我们来测试一下:

    #测试layer_sizes
    print("=========================测试layer_sizes=========================")
    X_asses , Y_asses = layer_sizes_test_case()
    (n_x,n_h,n_y) =  layer_sizes(X_asses,Y_asses)
    print("输入层的节点数量为: n_x = " + str(n_x))
    print("隐藏层的节点数量为: n_h = " + str(n_h))
    print("输出层的节点数量为: n_y = " + str(n_y))
    

    运行结果如下:

    =========================测试layer_sizes=========================
    输入层的节点数量为: n_x = 5
    隐藏层的节点数量为: n_h = 4
    输出层的节点数量为: n_y = 2
    

    初始化模型的参数

    在这里,我们要实现函数initialize_parameters()。我们要确保我们的参数大小合适,如果需要的话,请参考上面的神经网络图。
    我们将会用随机值初始化权重矩阵。

    • np.random.randn(a,b)* 0.01来随机初始化一个维度为(a,b)的矩阵。

    将偏向量初始化为零。

    • np.zeros((a,b))用零初始化矩阵(a,b)。
    def initialize_parameters( n_x , n_h ,n_y):
        """
        参数:
            n_x - 输入层节点的数量
            n_h - 隐藏层节点的数量
            n_y - 输出层节点的数量
        
        返回:
            parameters - 包含参数的字典:
                W1 - 权重矩阵,维度为(n_h,n_x)
                b1 - 偏向量,维度为(n_h,1)
                W2 - 权重矩阵,维度为(n_y,n_h)
                b2 - 偏向量,维度为(n_y,1)
    
        """
        np.random.seed(2) #指定一个随机种子,以便你的输出与我们的一样。
        W1 = np.random.randn(n_h,n_x) * 0.01
        b1 = np.zeros(shape=(n_h, 1))
        W2 = np.random.randn(n_y,n_h) * 0.01
        b2 = np.zeros(shape=(n_y, 1))
        
        #使用断言确保我的数据格式是正确的
        assert(W1.shape == ( n_h , n_x ))
        assert(b1.shape == ( n_h , 1 ))
        assert(W2.shape == ( n_y , n_h ))
        assert(b2.shape == ( n_y , 1 ))
        
        parameters = {"W1" : W1,
    	              "b1" : b1,
    	              "W2" : W2,
    	              "b2" : b2 }
        
        return parameters
    

    测试一下我们的代码:

    #测试initialize_parameters
    print("=========================测试initialize_parameters=========================")    
    n_x , n_h , n_y = initialize_parameters_test_case()
    parameters = initialize_parameters(n_x , n_h , n_y)
    print("W1 = " + str(parameters["W1"]))
    print("b1 = " + str(parameters["b1"]))
    print("W2 = " + str(parameters["W2"]))
    print("b2 = " + str(parameters["b2"]))
    
    

    结果如下:

    =========================测试initialize_parameters=========================
    W1 = [[-0.00416758 -0.00056267]
     [-0.02136196  0.01640271]
     [-0.01793436 -0.00841747]
     [ 0.00502881 -0.01245288]]
    b1 = [[ 0.]
     [ 0.]
     [ 0.]
     [ 0.]]
    W2 = [[-0.01057952 -0.00909008  0.00551454  0.02292208]]
    b2 = [[ 0.]]
    

    循环

    前向传播

    我们现在要实现前向传播函数forward_propagation()。
    我们可以使用sigmoid()函数,也可以使用np.tanh()函数。
    步骤如下:

    • 使用字典类型的parameters(它是initialize_parameters() 的输出)检索每个参数。
    • 实现向前传播, 计算 Z [ 1 ] , A [ 1 ] , Z [ 2 ] Z^{[1]}, A^{[1]}, Z^{[2]} Z[1],A[1],Z[2] A [ 2 ] A^{[2]} A[2]( 训练集里面所有例子的预测向量)。
    • 反向传播所需的值存储在“cache”中,cache将作为反向传播函数的输入。
    def forward_propagation( X , parameters ):
        """
        参数:
             X - 维度为(n_x,m)的输入数据。
             parameters - 初始化函数(initialize_parameters)的输出
        
        返回:
             A2 - 使用sigmoid()函数计算的第二次激活后的数值
             cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型变量
         """
        W1 = parameters["W1"]
        b1 = parameters["b1"]
        W2 = parameters["W2"]
        b2 = parameters["b2"]
        #前向传播计算A2
        Z1 = np.dot(W1 , X) + b1
        A1 = np.tanh(Z1)
        Z2 = np.dot(W2 , A1) + b2
        A2 = sigmoid(Z2)
        #使用断言确保我的数据格式是正确的
        assert(A2.shape == (1,X.shape[1]))
        cache = {"Z1": Z1,
                 "A1": A1,
                 "Z2": Z2,
                 "A2": A2}
        
        return (A2, cache)
    

    测试一下我的这个功能:

    #测试forward_propagation
    print("=========================测试forward_propagation=========================") 
    X_assess, parameters = forward_propagation_test_case()
    A2, cache = forward_propagation(X_assess, parameters)
    print(np.mean(cache["Z1"]), np.mean(cache["A1"]), np.mean(cache["Z2"]), np.mean(cache["A2"]))
    

    测试结果如下:

    =========================测试forward_propagation=========================
    -0.000499755777742 -0.000496963353232 0.000438187450959 0.500109546852
    

    现在我们已经计算了 A [ 2 ] A^{[2]} A[2] a [ 2 ] ( i ) a^{[2](i)} a[2](i)包含了训练集里每个数值,现在我们就可以构建成本函数了。

    计算损失

    计算成本的公式如下:
    J = − 1 m ∑ i = 0 m ( y ( i ) log ⁡ ( a [ 2 ] ( i ) ) + ( 1 − y ( i ) ) log ⁡ ( 1 − a [ 2 ] ( i ) ) ) (6) J = - \frac{1}{m} \sum\limits_{i = 0}^{m} \large\left(\small y^{(i)}\log\left(a^{[2] (i)}\right) + (1-y^{(i)})\log\left(1- a^{[2] (i)}\right) \large \right) \small \tag{6} J=m1i=0m(y(i)log(a[2](i))+(1y(i))log(1a[2](i)))(6)
    有很多的方法都可以计算交叉熵损失,比如下面的这个公式,我们在python中可以这么实现:
    KaTeX parse error: \tag works only in display equations:

    logprobs = np.multiply(np.log(A2),Y)
    cost = - np.sum(logprobs)                # 不需要使用循环就可以直接算出来。
    

    当然,你也可以使用np.multiply()然后使用np.sum()或者直接使用np.dot()
    现在我们正式开始构建计算成本的函数:

    def compute_cost(A2,Y,parameters):
        """
        计算方程(6)中给出的交叉熵成本,
        
        参数:
             A2 - 使用sigmoid()函数计算的第二次激活后的数值
             Y - "True"标签向量,维度为(1,数量)
             parameters - 一个包含W1,B1,W2和B2的字典类型的变量
        
        返回:
             成本 - 交叉熵成本给出方程(13)
        """
        
        m = Y.shape[1]
        W1 = parameters["W1"]
        W2 = parameters["W2"]
        
        #计算成本
        logprobs = logprobs = np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2))
        cost = - np.sum(logprobs) / m
        cost = float(np.squeeze(cost))
        
        assert(isinstance(cost,float))
        
        return cost
    

    测试一下我们的成本函数:

    #测试compute_cost
    print("=========================测试compute_cost=========================") 
    A2 , Y_assess , parameters = compute_cost_test_case()
    print("cost = " + str(compute_cost(A2,Y_assess,parameters)))
    

    测试结果如下:

    =========================测试compute_cost=========================
    cost = 0.6929198937761266
    

    使用正向传播期间计算的cache,现在可以利用它实现反向传播。

    现在我们要开始实现函数backward_propagation()。

    向后传播

      说明:反向传播通常是深度学习中最难(数学意义)部分,为了帮助你,这里有反向传播讲座的幻灯片, 由于我们正在构建向量化实现,因此我们将需要使用这下面的六个方程:
    Summary of gradient desent
    为了计算dZ1,里需要计算 g [ 1 ] ′ ( Z [ 1 ] ) g^{[1]'}(Z^{[1]}) g[1](Z[1]) g [ 1 ] ( . . . ) g^{[1]}(...) g[1](...) 是tanh激活函数,如果 a = g [ 1 ] ( z ) a = g^{[1]}(z) a=g[1](z) 那么 g [ 1 ] ′ ( z ) = 1 − a 2 g^{[1]'}(z) = 1-a^2 g[1](z)=1a2。所以我们需要使用 (1 - np.power(A1, 2))来计算 g [ 1 ] ′ ( Z [ 1 ] ) g^{[1]'}(Z^{[1]}) g[1](Z[1])

    def backward_propagation(parameters,cache,X,Y):
        """
        使用上述说明搭建反向传播函数。
        
        参数:
         parameters - 包含我们的参数的一个字典类型的变量。
         cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型的变量。
         X - 输入数据,维度为(2,数量)
         Y - “True”标签,维度为(1,数量)
        
        返回:
         grads - 包含W和b的导数一个字典类型的变量。
        """
        m = X.shape[1]
        
        W1 = parameters["W1"]
        W2 = parameters["W2"]
        
        A1 = cache["A1"]
        A2 = cache["A2"]
        
        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
    

    测试一下反向传播函数:

    #测试backward_propagation
    print("=========================测试backward_propagation=========================")
    parameters, cache, X_assess, Y_assess = backward_propagation_test_case()
    
    grads = backward_propagation(parameters, cache, X_assess, Y_assess)
    print ("dW1 = "+ str(grads["dW1"]))
    print ("db1 = "+ str(grads["db1"]))
    print ("dW2 = "+ str(grads["dW2"]))
    print ("db2 = "+ str(grads["db2"]))
    

    测试结果如下:

    =========================测试backward_propagation=========================
    dW1 = [[ 0.01018708 -0.00708701]
     [ 0.00873447 -0.0060768 ]
     [-0.00530847  0.00369379]
     [-0.02206365  0.01535126]]
    db1 = [[-0.00069728]
     [-0.00060606]
     [ 0.000364  ]
     [ 0.00151207]]
    dW2 = [[ 0.00363613  0.03153604  0.01162914 -0.01318316]]
    db2 = [[ 0.06589489]]
    

    反向传播完成了,我们开始对参数进行更新

    更新参数

    我们需要使用(dW1, db1, dW2, db2)来更新(W1, b1, W2, b2)。
    更新算法如下:
    $ \theta = \theta - \alpha \frac{\partial J }{ \partial \theta }$

    • α \alpha α:学习速率
    • θ \theta θ :参数

    我们需要选择一个良好的学习速率,我们可以看一下下面这两个图(由Adam Harley提供):
    sgdsgd_bad
    上面两个图分别代表了具有良好学习速率(收敛)和不良学习速率(发散)的梯度下降算法。

    def update_parameters(parameters,grads,learning_rate=1.2):
        """
        使用上面给出的梯度下降更新规则更新参数
        
        参数:
         parameters - 包含参数的字典类型的变量。
         grads - 包含导数值的字典类型的变量。
         learning_rate - 学习速率
        
        返回:
         parameters - 包含更新参数的字典类型的变量。
        """
        W1,W2 = parameters["W1"],parameters["W2"]
        b1,b2 = parameters["b1"],parameters["b2"]
        
        dW1,dW2 = grads["dW1"],grads["dW2"]
        db1,db2 = grads["db1"],grads["db2"]
        
        W1 = W1 - learning_rate * dW1
        b1 = b1 - learning_rate * db1
        W2 = W2 - learning_rate * dW2
        b2 = b2 - learning_rate * db2
        
        parameters = {"W1": W1,
                      "b1": b1,
                      "W2": W2,
                      "b2": b2}
        
        return parameters
    

    测试一下update_parameters():

    #测试update_parameters
    print("=========================测试update_parameters=========================")
    parameters, grads = update_parameters_test_case()
    parameters = update_parameters(parameters, grads)
    
    print("W1 = " + str(parameters["W1"]))
    print("b1 = " + str(parameters["b1"]))
    print("W2 = " + str(parameters["W2"]))
    print("b2 = " + str(parameters["b2"]))
    

    测试结果如下:

    =========================测试update_parameters=========================
    W1 = [[-0.00643025  0.01936718]
     [-0.02410458  0.03978052]
     [-0.01653973 -0.02096177]
     [ 0.01046864 -0.05990141]]
    b1 = [[ -1.02420756e-06]
     [  1.27373948e-05]
     [  8.32996807e-07]
     [ -3.20136836e-06]]
    W2 = [[-0.01041081 -0.04463285  0.01758031  0.04747113]]
    b2 = [[ 0.00010457]]
    

    整合

    我们现在把上面的东西整合到nn_model()中,神经网络模型必须以正确的顺序使用先前的功能。

    def nn_model(X,Y,n_h,num_iterations,print_cost=False):
        """
        参数:
            X - 数据集,维度为(2,示例数)
            Y - 标签,维度为(1,示例数)
            n_h - 隐藏层的数量
            num_iterations - 梯度下降循环中的迭代次数
            print_cost - 如果为True,则每1000次迭代打印一次成本数值
        
        返回:
            parameters - 模型学习的参数,它们可以用来进行预测。
         """
         
        np.random.seed(3) #指定随机种子
        n_x = layer_sizes(X, Y)[0]
        n_y = layer_sizes(X, Y)[2]
        
        parameters = initialize_parameters(n_x,n_h,n_y)
        W1 = parameters["W1"]
        b1 = parameters["b1"]
        W2 = parameters["W2"]
        b2 = parameters["b2"]
        
        for i in range(num_iterations):
            A2 , cache = forward_propagation(X,parameters)
            cost = compute_cost(A2,Y,parameters)
            grads = backward_propagation(parameters,cache,X,Y)
            parameters = update_parameters(parameters,grads,learning_rate = 0.5)
            
            if print_cost:
                if i%1000 == 0:
                    print("第 ",i," 次循环,成本为:"+str(cost))
        return parameters
    

    测试nn_model():

    #测试nn_model
    print("=========================测试nn_model=========================")
    X_assess, Y_assess = nn_model_test_case()
    
    parameters = nn_model(X_assess, Y_assess, 4, num_iterations=10000, print_cost=False)
    print("W1 = " + str(parameters["W1"]))
    print("b1 = " + str(parameters["b1"]))
    print("W2 = " + str(parameters["W2"]))
    print("b2 = " + str(parameters["b2"]))
    

    测试结果如下:

    =========================测试nn_model=========================
    W1 = [[-4.18494482  5.33220319]
     [-7.52989354  1.24306197]
     [-4.19295428  5.32631786]
     [ 7.52983748 -1.24309404]]
    b1 = [[ 2.32926815]
     [ 3.7945905 ]
     [ 2.33002544]
     [-3.79468791]]
    W2 = [[-6033.83672179 -6008.12981272 -6033.10095329  6008.06636901]]
    b2 = [[-52.66607704]]
    

    参数更新完了我们就可以来进行预测了。

    预测

    构建predict()来使用模型进行预测, 使用向前传播来预测结果。
    predictions = KaTeX parse error: Undefined control sequence: \ at position 46: …1 & 激活值> 0.5 \\\̲ ̲ 0 & \text…

    def predict(parameters,X):
        """
        使用学习的参数,为X中的每个示例预测一个类
        
        参数:
    		parameters - 包含参数的字典类型的变量。
    	    X - 输入数据(n_x,m)
        
        返回
    		predictions - 我们模型预测的向量(红色:0 /蓝色:1)
         
         """
        A2 , cache = forward_propagation(X,parameters)
        predictions = np.round(A2)
        
        return predictions
    

    测试一下predict

    #测试predict
    print("=========================测试predict=========================")
    
    parameters, X_assess = predict_test_case()
    
    predictions = predict(parameters, X_assess)
    print("预测的平均值 = " + str(np.mean(predictions)))
    

    测试结果:

    =========================测试predict=========================
    预测的平均值 = 0.666666666667
    

    现在我们把所有的东西基本都做完了,我们开始正式运行。


    正式运行

    parameters = nn_model(X, Y, n_h = 4, num_iterations=10000, print_cost=True)
    
    #绘制边界
    plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
    plt.title("Decision Boundary for hidden layer size " + str(4))
    
    predictions = predict(parameters, X)
    print ('准确率: %d' % float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) + '%')
    

    运行结果:

    0  次循环,成本为:0.69304802012398231000  次循环,成本为:0.288083293569018352000  次循环,成本为:0.254385494073244963000  次循环,成本为:0.233864150389521964000  次循环,成本为:0.226792487448540085000  次循环,成本为:0.222644275492990156000  次循环,成本为:0.219731404042813167000  次循环,成本为:0.217503654051312948000  次循环,成本为:0.219503964694673159000  次循环,成本为:0.2185709575018246
    准确率: 90%
    

    resulat


    更改隐藏层节点数量

    我们上面的实验把隐藏层定为4个节点,现在我们更改隐藏层里面的节点数量,看一看节点数量是否会对结果造成影响。

    plt.figure(figsize=(16, 32))
    hidden_layer_sizes = [1, 2, 3, 4, 5, 20, 50] #隐藏层数量
    for i, n_h in enumerate(hidden_layer_sizes):
        plt.subplot(5, 2, i + 1)
        plt.title('Hidden Layer of size %d' % n_h)
        parameters = nn_model(X, Y, n_h, num_iterations=5000)
        plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
        predictions = predict(parameters, X)
        accuracy = float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100)
        print ("隐藏层的节点数量: {}  ,准确率: {} %".format(n_h, accuracy))
    

    打印结果:

    隐藏层的节点数量: 1  ,准确率: 67.5 %
    隐藏层的节点数量: 2  ,准确率: 67.25 %
    隐藏层的节点数量: 3  ,准确率: 90.75 %
    隐藏层的节点数量: 4  ,准确率: 90.5 %
    隐藏层的节点数量: 5  ,准确率: 91.25 %
    隐藏层的节点数量: 20  ,准确率: 90.0 %
    隐藏层的节点数量: 50  ,准确率: 90.75 %
    

    units

    较大的模型(具有更多隐藏单元)能够更好地适应训练集,直到最终的最大模型过度拟合数据。
    最好的隐藏层大小似乎在n_h = 5附近。实际上,这里的值似乎很适合数据,而且不会引起过度拟合。
    我们还将在后面学习有关正则化的知识,它允许我们使用非常大的模型(如n_h = 50),而不会出现太多过度拟合。


    ##【可选】探索

    • 当改变sigmoid激活或ReLU激活的tanh激活时会发生什么?
    • 改变learning_rate的数值会发生什么
    • 如果我们改变数据集呢?
    # 数据集
    noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure = load_extra_datasets()
    
    datasets = {"noisy_circles": noisy_circles,
                "noisy_moons": noisy_moons,
                "blobs": blobs,
                "gaussian_quantiles": gaussian_quantiles}
    
    dataset = "noisy_moons"
    
    X, Y = datasets[dataset]
    X, Y = X.T, Y.reshape(1, Y.shape[0])
    
    if dataset == "blobs":
        Y = Y % 2
    
    plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral)
    
    #上一语句如出现问题请使用下面的语句:
    plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)
    

    new dataset


    完整代码

    作业代码

    # -*- coding: utf-8 -*-
    """
    本文博客地址:https://blog.csdn.net/u013733326/article/details/79702148
    
    @author: Oscar
    """
    
    import numpy as np
    import matplotlib.pyplot as plt
    from testCases import *
    import sklearn
    import sklearn.datasets
    import sklearn.linear_model
    from planar_utils import plot_decision_boundary, sigmoid, load_planar_dataset, load_extra_datasets
    
    #%matplotlib inline #如果你使用用的是Jupyter Notebook的话请取消注释。
    
    np.random.seed(1) #设置一个固定的随机种子,以保证接下来的步骤中我们的结果是一致的。
    
    X, Y = load_planar_dataset()
    #plt.scatter(X[0, :], X[1, :], c=Y, s=40, cmap=plt.cm.Spectral) #绘制散点图
    shape_X = X.shape
    shape_Y = Y.shape
    m = Y.shape[1]  # 训练集里面的数量
    
    print ("X的维度为: " + str(shape_X))
    print ("Y的维度为: " + str(shape_Y))
    print ("数据集里面的数据有:" + str(m) + " 个")
    
    def layer_sizes(X , Y):
        """
        参数:
         X - 输入数据集,维度为(输入的数量,训练/测试的数量)
         Y - 标签,维度为(输出的数量,训练/测试数量)
    
        返回:
         n_x - 输入层的数量
         n_h - 隐藏层的数量
         n_y - 输出层的数量
        """
        n_x = X.shape[0] #输入层
        n_h = 4 #,隐藏层,硬编码为4
        n_y = Y.shape[0] #输出层
    
        return (n_x,n_h,n_y)
    
    def initialize_parameters( n_x , n_h ,n_y):
        """
        参数:
            n_x - 输入节点的数量
            n_h - 隐藏层节点的数量
            n_y - 输出层节点的数量
    
        返回:
            parameters - 包含参数的字典:
                W1 - 权重矩阵,维度为(n_h,n_x)
                b1 - 偏向量,维度为(n_h,1)
                W2 - 权重矩阵,维度为(n_y,n_h)
                b2 - 偏向量,维度为(n_y,1)
    
        """
        np.random.seed(2) #指定一个随机种子,以便你的输出与我们的一样。
        W1 = np.random.randn(n_h,n_x) * 0.01
        b1 = np.zeros(shape=(n_h, 1))
        W2 = np.random.randn(n_y,n_h) * 0.01
        b2 = np.zeros(shape=(n_y, 1))
    
        #使用断言确保我的数据格式是正确的
        assert(W1.shape == ( n_h , n_x ))
        assert(b1.shape == ( n_h , 1 ))
        assert(W2.shape == ( n_y , n_h ))
        assert(b2.shape == ( n_y , 1 ))
    
        parameters = {"W1" : W1,
                      "b1" : b1,
                      "W2" : W2,
                      "b2" : b2 }
    
        return parameters
    
    def forward_propagation( X , parameters ):
        """
        参数:
             X - 维度为(n_x,m)的输入数据。
             parameters - 初始化函数(initialize_parameters)的输出
    
        返回:
             A2 - 使用sigmoid()函数计算的第二次激活后的数值
             cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型变量
         """
        W1 = parameters["W1"]
        b1 = parameters["b1"]
        W2 = parameters["W2"]
        b2 = parameters["b2"]
        #前向传播计算A2
        Z1 = np.dot(W1 , X) + b1
        A1 = np.tanh(Z1)
        Z2 = np.dot(W2 , A1) + b2
        A2 = sigmoid(Z2)
        #使用断言确保我的数据格式是正确的
        assert(A2.shape == (1,X.shape[1]))
        cache = {"Z1": Z1,
                 "A1": A1,
                 "Z2": Z2,
                 "A2": A2}
    
        return (A2, cache)
    
    def compute_cost(A2,Y,parameters):
        """
        计算方程(6)中给出的交叉熵成本,
    
        参数:
             A2 - 使用sigmoid()函数计算的第二次激活后的数值
             Y - "True"标签向量,维度为(1,数量)
             parameters - 一个包含W1,B1,W2和B2的字典类型的变量
    
        返回:
             成本 - 交叉熵成本给出方程(13)
        """
    
        m = Y.shape[1]
        W1 = parameters["W1"]
        W2 = parameters["W2"]
    
        #计算成本
        logprobs = logprobs = np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2))
        cost = - np.sum(logprobs) / m
        cost = float(np.squeeze(cost))
    
        assert(isinstance(cost,float))
    
        return cost
    
    def backward_propagation(parameters,cache,X,Y):
        """
        使用上述说明搭建反向传播函数。
    
        参数:
         parameters - 包含我们的参数的一个字典类型的变量。
         cache - 包含“Z1”,“A1”,“Z2”和“A2”的字典类型的变量。
         X - 输入数据,维度为(2,数量)
         Y - “True”标签,维度为(1,数量)
    
        返回:
         grads - 包含W和b的导数一个字典类型的变量。
        """
        m = X.shape[1]
    
        W1 = parameters["W1"]
        W2 = parameters["W2"]
    
        A1 = cache["A1"]
        A2 = cache["A2"]
    
        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
    
    def update_parameters(parameters,grads,learning_rate=1.2):
        """
        使用上面给出的梯度下降更新规则更新参数
    
        参数:
         parameters - 包含参数的字典类型的变量。
         grads - 包含导数值的字典类型的变量。
         learning_rate - 学习速率
    
        返回:
         parameters - 包含更新参数的字典类型的变量。
        """
        W1,W2 = parameters["W1"],parameters["W2"]
        b1,b2 = parameters["b1"],parameters["b2"]
    
        dW1,dW2 = grads["dW1"],grads["dW2"]
        db1,db2 = grads["db1"],grads["db2"]
    
        W1 = W1 - learning_rate * dW1
        b1 = b1 - learning_rate * db1
        W2 = W2 - learning_rate * dW2
        b2 = b2 - learning_rate * db2
    
        parameters = {"W1": W1,
                      "b1": b1,
                      "W2": W2,
                      "b2": b2}
    
        return parameters
    
    def nn_model(X,Y,n_h,num_iterations,print_cost=False):
        """
        参数:
            X - 数据集,维度为(2,示例数)
            Y - 标签,维度为(1,示例数)
            n_h - 隐藏层的数量
            num_iterations - 梯度下降循环中的迭代次数
            print_cost - 如果为True,则每1000次迭代打印一次成本数值
    
        返回:
            parameters - 模型学习的参数,它们可以用来进行预测。
         """
    
        np.random.seed(3) #指定随机种子
        n_x = layer_sizes(X, Y)[0]
        n_y = layer_sizes(X, Y)[2]
    
        parameters = initialize_parameters(n_x,n_h,n_y)
        W1 = parameters["W1"]
        b1 = parameters["b1"]
        W2 = parameters["W2"]
        b2 = parameters["b2"]
    
        for i in range(num_iterations):
            A2 , cache = forward_propagation(X,parameters)
            cost = compute_cost(A2,Y,parameters)
            grads = backward_propagation(parameters,cache,X,Y)
            parameters = update_parameters(parameters,grads,learning_rate = 0.5)
    
            if print_cost:
                if i%1000 == 0:
                    print("第 ",i," 次循环,成本为:"+str(cost))
        return parameters
    
    def predict(parameters,X):
        """
        使用学习的参数,为X中的每个示例预测一个类
    
        参数:
            parameters - 包含参数的字典类型的变量。
            X - 输入数据(n_x,m)
    
        返回
            predictions - 我们模型预测的向量(红色:0 /蓝色:1)
    
         """
        A2 , cache = forward_propagation(X,parameters)
        predictions = np.round(A2)
    
        return predictions
    
    parameters = nn_model(X, Y, n_h = 4, num_iterations=10000, print_cost=True)
    
    #绘制边界
    plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
    plt.title("Decision Boundary for hidden layer size " + str(4))
    
    predictions = predict(parameters, X)
    print ('准确率: %d' % float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100) + '%')
    
    """
    plt.figure(figsize=(16, 32))
    hidden_layer_sizes = [1, 2, 3, 4, 5, 20, 50] #隐藏层数量
    for i, n_h in enumerate(hidden_layer_sizes):
        plt.subplot(5, 2, i + 1)
        plt.title('Hidden Layer of size %d' % n_h)
        parameters = nn_model(X, Y, n_h, num_iterations=5000)
        plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
        predictions = predict(parameters, X)
        accuracy = float((np.dot(Y, predictions.T) + np.dot(1 - Y, 1 - predictions.T)) / float(Y.size) * 100)
        print ("隐藏层的节点数量: {}  ,准确率: {} %".format(n_h, accuracy))
    """
    

    testCases.py

    #-*- coding: UTF-8 -*-
    """
    # WANGZHE12
    """
    import numpy as np
    
    def layer_sizes_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(5, 3)
        Y_assess = np.random.randn(2, 3)
        return X_assess, Y_assess
    
    def initialize_parameters_test_case():
        n_x, n_h, n_y = 2, 4, 1
        return n_x, n_h, n_y
    
    def forward_propagation_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
    
        parameters = {'W1': np.array([[-0.00416758, -0.00056267],
            [-0.02136196,  0.01640271],
            [-0.01793436, -0.00841747],
            [ 0.00502881, -0.01245288]]),
         'W2': np.array([[-0.01057952, -0.00909008,  0.00551454,  0.02292208]]),
         'b1': np.array([[ 0.],
            [ 0.],
            [ 0.],
            [ 0.]]),
         'b2': np.array([[ 0.]])}
    
        return X_assess, parameters
    
    def compute_cost_test_case():
        np.random.seed(1)
        Y_assess = np.random.randn(1, 3)
        parameters = {'W1': np.array([[-0.00416758, -0.00056267],
            [-0.02136196,  0.01640271],
            [-0.01793436, -0.00841747],
            [ 0.00502881, -0.01245288]]),
         'W2': np.array([[-0.01057952, -0.00909008,  0.00551454,  0.02292208]]),
         'b1': np.array([[ 0.],
            [ 0.],
            [ 0.],
            [ 0.]]),
         'b2': np.array([[ 0.]])}
    
        a2 = (np.array([[ 0.5002307 ,  0.49985831,  0.50023963]]))
    
        return a2, Y_assess, parameters
    
    def backward_propagation_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
        Y_assess = np.random.randn(1, 3)
        parameters = {'W1': np.array([[-0.00416758, -0.00056267],
            [-0.02136196,  0.01640271],
            [-0.01793436, -0.00841747],
            [ 0.00502881, -0.01245288]]),
         'W2': np.array([[-0.01057952, -0.00909008,  0.00551454,  0.02292208]]),
         'b1': np.array([[ 0.],
            [ 0.],
            [ 0.],
            [ 0.]]),
         'b2': np.array([[ 0.]])}
    
        cache = {'A1': np.array([[-0.00616578,  0.0020626 ,  0.00349619],
             [-0.05225116,  0.02725659, -0.02646251],
             [-0.02009721,  0.0036869 ,  0.02883756],
             [ 0.02152675, -0.01385234,  0.02599885]]),
      'A2': np.array([[ 0.5002307 ,  0.49985831,  0.50023963]]),
      'Z1': np.array([[-0.00616586,  0.0020626 ,  0.0034962 ],
             [-0.05229879,  0.02726335, -0.02646869],
             [-0.02009991,  0.00368692,  0.02884556],
             [ 0.02153007, -0.01385322,  0.02600471]]),
      'Z2': np.array([[ 0.00092281, -0.00056678,  0.00095853]])}
        return parameters, cache, X_assess, Y_assess
    
    def update_parameters_test_case():
        parameters = {'W1': np.array([[-0.00615039,  0.0169021 ],
            [-0.02311792,  0.03137121],
            [-0.0169217 , -0.01752545],
            [ 0.00935436, -0.05018221]]),
     'W2': np.array([[-0.0104319 , -0.04019007,  0.01607211,  0.04440255]]),
     'b1': np.array([[ -8.97523455e-07],
            [  8.15562092e-06],
            [  6.04810633e-07],
            [ -2.54560700e-06]]),
     'b2': np.array([[  9.14954378e-05]])}
    
        grads = {'dW1': np.array([[ 0.00023322, -0.00205423],
            [ 0.00082222, -0.00700776],
            [-0.00031831,  0.0028636 ],
            [-0.00092857,  0.00809933]]),
     'dW2': np.array([[ -1.75740039e-05,   3.70231337e-03,  -1.25683095e-03,
              -2.55715317e-03]]),
     'db1': np.array([[  1.05570087e-07],
            [ -3.81814487e-06],
            [ -1.90155145e-07],
            [  5.46467802e-07]]),
     'db2': np.array([[ -1.08923140e-05]])}
        return parameters, grads
    
    def nn_model_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
        Y_assess = np.random.randn(1, 3)
        return X_assess, Y_assess
    
    def predict_test_case():
        np.random.seed(1)
        X_assess = np.random.randn(2, 3)
        parameters = {'W1': np.array([[-0.00615039,  0.0169021 ],
            [-0.02311792,  0.03137121],
            [-0.0169217 , -0.01752545],
            [ 0.00935436, -0.05018221]]),
         'W2': np.array([[-0.0104319 , -0.04019007,  0.01607211,  0.04440255]]),
         'b1': np.array([[ -8.97523455e-07],
            [  8.15562092e-06],
            [  6.04810633e-07],
            [ -2.54560700e-06]]),
         'b2': np.array([[  9.14954378e-05]])}
        return parameters, X_assess
    
    

    planar_utils.py

    import matplotlib.pyplot as plt
    import numpy as np
    import sklearn
    import sklearn.datasets
    import sklearn.linear_model
    
    def plot_decision_boundary(model, X, y):
        # Set min and max values and give it some padding
        x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
        y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
        h = 0.01
        # Generate a grid of points with distance h between them
        xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
        # Predict the function value for the whole grid
        Z = model(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        # Plot the contour and training examples
        plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
        plt.ylabel('x2')
        plt.xlabel('x1')
        plt.scatter(X[0, :], X[1, :], c=np.squeeze(y), cmap=plt.cm.Spectral)
    
    
    def sigmoid(x):
        s = 1/(1+np.exp(-x))
        return s
    
    def load_planar_dataset():
        np.random.seed(1)
        m = 400 # number of examples
        N = int(m/2) # number of points per class
        D = 2 # dimensionality
        X = np.zeros((m,D)) # data matrix where each row is a single example
        Y = np.zeros((m,1), dtype='uint8') # labels vector (0 for red, 1 for blue)
        a = 4 # maximum ray of the flower
    
        for j in range(2):
            ix = range(N*j,N*(j+1))
            t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2 # theta
            r = a*np.sin(4*t) + np.random.randn(N)*0.2 # radius
            X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
            Y[ix] = j
    
        X = X.T
        Y = Y.T
    
        return X, Y
    
    def load_extra_datasets():  
        N = 200
        noisy_circles = sklearn.datasets.make_circles(n_samples=N, factor=.5, noise=.3)
        noisy_moons = sklearn.datasets.make_moons(n_samples=N, noise=.2)
        blobs = sklearn.datasets.make_blobs(n_samples=N, random_state=5, n_features=2, centers=6)
        gaussian_quantiles = sklearn.datasets.make_gaussian_quantiles(mean=None, cov=0.5, n_samples=N, n_features=2, n_classes=2, shuffle=True, random_state=None)
        no_structure = np.random.rand(N, 2), np.random.rand(N, 2)
    
        return noisy_circles, noisy_moons, blobs, gaussian_quantiles, no_structure
    
    展开全文
  • 【中文】【吴恩达课后编程作业】Course 4 - 卷积神经网络 - 第二周作业 - Keras入门与残差网络的搭建 1 - Keras 入门 - 笑脸识别 本次我们将: 1. 学习到一个高级的神经网络的框架,能够运行在包括...

    【中文】【吴恩达课后编程作业】Course 4 - 卷积神经网络 - 第二周作业 - Keras入门与残差网络的搭建


    上一篇: 【课程4 - 第二周测验】※※※※※ 【回到目录】※※※※※下一篇: 【课程4 - 第三周测验】

    资料下载

    • 下载1:本文所使用的资料已上传到百度网盘【点击下载(15.97MB)】,提取码:hb3y ,请在开始之前下载好所需资料,或者在本文底部copy资料代码。

    • 下载2(有偿下载):在残差网络中博主花费大力气训练好了残差网络的权值,这部分是需要有偿下载的,需要使用C币进行下载,下载地址:https://download.csdn.net/download/u013733326/10403196

    • 下载2(无偿下载):在残差网络中博主花费大力气训练好了残差网络的权值,这部分我是万万没想到从3C币怎么变成了15C币的,现免费下载:链接:https://pan.baidu.com/s/19lxD9573BmG7sC3IUJXeHg&shfl=sharepset ,提取码:5ev7


    【博主使用的python版本:3.6.2】


    1 - Keras 入门 - 笑脸识别

    本次我们将:

    1. 学习到一个高级的神经网络的框架,能够运行在包括TensorFlow和CNTK的几个较低级别的框架之上的框架。
    2. 看看如何在几个小时内建立一个深入的学习算法。

      为什么我们要使用Keras框架呢?Keras是为了使深度学习工程师能够很快地建立和实验不同的模型的框架,正如TensorFlow是一个比Python更高级的框架,Keras是一个更高层次的框架,并提供了额外的抽象方法。最关键的是Keras能够以最短的时间让想法变为现实。然而,Keras比底层框架更具有限制性,所以有一些非常复杂的模型可以在TensorFlow中实现,但在Keras中却没有(没有更多困难)。 话虽如此,Keras对许多常见模型都能正常运行。

    import numpy as np
    from keras import layers
    from keras.layers import Input, Dense, Activation, ZeroPadding2D, BatchNormalization, Flatten, Conv2D
    from keras.layers import AveragePooling2D, MaxPooling2D, Dropout, GlobalMaxPooling2D, GlobalAveragePooling2D
    from keras.models import Model
    from keras.preprocessing import image
    from keras.utils import layer_utils
    from keras.utils.data_utils import get_file
    from keras.applications.imagenet_utils import preprocess_input
    import pydot
    from IPython.display import SVG
    from keras.utils.vis_utils import model_to_dot
    from keras.utils import plot_model
    import kt_utils 
    
    import keras.backend as K
    K.set_image_data_format('channels_last')
    import matplotlib.pyplot as plt
    from matplotlib.pyplot import imshow
    
    %matplotlib inline
    

    注意:正如你所看到的,我们已经从Keras中导入了很多功能, 只需直接调用它们即可轻松使用它们。 比如:X = Input(…) 或者X = ZeroPadding2D(…).

    1.1 - 任务描述

      下一次放假的时候,你决定和你的五个朋友一起度过一个星期。这是一个非常好的房子,在附近有很多事情要做,但最重要的好处是每个人在家里都会感到快乐,所以任何想进入房子的人都必须证明他们目前的幸福状态。

      作为一个深度学习的专家,为了确保“快乐才开门”规则得到严格的应用,你将建立一个算法,它使用来自前门摄像头的图片来检查这个人是否快乐,只有在人高兴的时候,门才会打开。

    happy-house

    **Figure 1** : **the Happy House**

    你收集了你的朋友和你自己的照片,被前门的摄像头拍了下来。数据集已经标记好了。。

    house-members.png

    我们先来加载数据集:

    X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = kt_utils load_dataset()
    
    # Normalize image vectors
    X_train = X_train_orig/255.
    X_test = X_test_orig/255.
    
    # Reshape
    Y_train = Y_train_orig.T
    Y_test = Y_test_orig.T
    
    print ("number of training examples = " + str(X_train.shape[0]))
    print ("number of test examples = " + str(X_test.shape[0]))
    print ("X_train shape: " + str(X_train.shape))
    print ("Y_train shape: " + str(Y_train.shape))
    print ("X_test shape: " + str(X_test.shape))
    print ("Y_test shape: " + str(Y_test.shape))
    

    执行结果

    number of training examples = 600
    number of test examples = 150
    X_train shape: (600, 64, 64, 3)
    Y_train shape: (600, 1)
    X_test shape: (150, 64, 64, 3)
    Y_test shape: (150, 1)
    

    数据集的细节如下:

    • 图像维度:(64,64,3)
    • 训练集数量:600
    • 测试集数量:150

    1.2 - 使用Keras框架构建模型

    Keras非常适合快速制作模型,它可以在很短的时间内建立一个很优秀的模型,举个例子:

    def model(input_shape):
    	"""
    	模型大纲
    	"""
        #定义一个tensor的placeholder,维度为input_shape
        X_input = Input(input_shape)
        
        #使用0填充:X_input的周围填充0
        X = ZeroPadding2D((3,3))(X_input)
        
        # 对X使用 CONV -> BN -> RELU 块
        X = Conv2D(32, (7, 7), strides = (1, 1), name = 'conv0')(X)
        X = BatchNormalization(axis = 3, name = 'bn0')(X)
        X = Activation('relu')(X)
        
        #最大值池化层
        X = MaxPooling2D((2,2),name="max_pool")(X)
        
        #降维,矩阵转化为向量 + 全连接层
        X = Flatten()(X)
        X = Dense(1, activation='sigmoid', name='fc')(X)
        
        #创建模型,讲话创建一个模型的实体,我们可以用它来训练、测试。
        model = Model(inputs = X_input, outputs = X, name='HappyModel')
        
        return model
        
    

      请注意:Keras框架使用的变量名和我们以前使用的numpy和TensorFlow变量不一样。它不是在前向传播的每一步上创建新变量(比如X, Z1, A1, Z2, A2,…)以便于不同层之间的计算。在Keras中,我们使用X覆盖了所有的值,没有保存每一层结果,我们只需要最新的值,唯一例外的就是X_input,我们将它分离出来是因为它是输入的数据,我们要在最后的创建模型那一步中用到。

    def HappyModel(input_shape):
        """
        实现一个检测笑容的模型
        
        参数:
            input_shape - 输入的数据的维度
        返回:
            model - 创建的Keras的模型
            
        """
        
        #你可以参考和上面的大纲
        X_input = Input(input_shape)
    
        #使用0填充:X_input的周围填充0
        X = ZeroPadding2D((3, 3))(X_input)
    
        #对X使用 CONV -> BN -> RELU 块
        X = Conv2D(32, (7, 7), strides=(1, 1), name='conv0')(X)
        X = BatchNormalization(axis=3, name='bn0')(X)
        X = Activation('relu')(X)
    
        #最大值池化层
        X = MaxPooling2D((2, 2), name='max_pool')(X)
    
        #降维,矩阵转化为向量 + 全连接层
        X = Flatten()(X)
        X = Dense(1, activation='sigmoid', name='fc')(X)
    
        #创建模型,讲话创建一个模型的实体,我们可以用它来训练、测试。
        model = Model(inputs=X_input, outputs=X, name='HappyModel')
    
        return model
    

    现在我们已经设计好了我们的模型了,要训练并测试模型我们需要这么做:

    1. 创建一个模型实体。
    2. 编译模型,可以使用这个语句:model.compile(optimizer = "...", loss = "...", metrics = ["accuracy"])
    3. 训练模型:model.fit(x = ..., y = ..., epochs = ..., batch_size = ...)
    4. 评估模型:model.evaluate(x = ..., y = ...)

    如果你想要获取关于model.compile(), model.fit(), model.evaluate()的更多的信息,你可以参考这里

    #创建一个模型实体
    happy_model = HappyModel(X_train.shape[1:])
    #编译模型
    happy_model.compile("adam","binary_crossentropy", metrics=['accuracy'])
    #训练模型
    #请注意,此操作会花费你大约6-10分钟。
    happy_model.fit(X_train, Y_train, epochs=40, batch_size=50)
    #评估模型
    preds = happy_model.evaluate(X_test, Y_test, batch_size=32, verbose=1, sample_weight=None)
    print ("误差值 = " + str(preds[0]))
    print ("准确度 = " + str(preds[1]))
    

    执行结果

    Epoch 1/40
    600/600 [==============================] - 12s 19ms/step - loss: 2.2593 - acc: 0.5667
    Epoch 2/40
    600/600 [==============================] - 9s 16ms/step - loss: 0.5355 - acc: 0.7917
    Epoch 3/40
    600/600 [==============================] - 10s 17ms/step - loss: 0.3252 - acc: 0.8650
    Epoch 4/40
    600/600 [==============================] - 10s 17ms/step - loss: 0.2038 - acc: 0.9250
    Epoch 5/40
    600/600 [==============================] - 10s 16ms/step - loss: 0.1664 - acc: 0.9333
    
    ...
    
    Epoch 38/40
    600/600 [==============================] - 10s 17ms/step - loss: 0.0173 - acc: 0.9950
    Epoch 39/40
    600/600 [==============================] - 14s 23ms/step - loss: 0.0365 - acc: 0.9883
    Epoch 40/40
    600/600 [==============================] - 12s 19ms/step - loss: 0.0291 - acc: 0.9900
    150/150 [==============================] - 3s 21ms/step
    误差值 = 0.407454126676
    准确度 = 0.840000001589
    

    只要准确度大于75%就算正常,如果你的准确度没有大于75%,你可以尝试改变模型:

    X = Conv2D(32, (3, 3), strides = (1, 1), name = 'conv0')(X)
    X = BatchNormalization(axis = 3, name = 'bn0')(X)
    X = Activation('relu')(X)
    
    • 你可以在每个块后面使用最大值池化层,它将会减少宽、高的维度。
    • 改变优化器,这里我们使用的是Adam
    • 如果模型难以运行,并且遇到了内存不够的问题,那么就降低batch_size(12通常是一个很好的折中方案)
    • 运行更多代,直到看到有良好效果的时候。

    即使你已经达到了75%的准确度,你也可以继续优化你的模型,以获得更好的结果。

    1.3 - 总结

    这个任务算是完成了,你可以在你家试试[手动滑稽]

    1.4 - 测试你的图片

    因为对这些数据进行训练的模型可能或不能处理你自己的图片,但是你可以试一试嘛:

    #网上随便找的图片,侵删
    img_path = 'images/smile.jpeg'
    
    img = image.load_img(img_path, target_size=(64, 64))
    imshow(img)
    
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)
    
    print(happy_model.predict(x))
    

    测试结果

    [[ 1.]]
    
    img_path = 'images/my_image.jpg'
    
    img = image.load_img(img_path, target_size=(64, 64))
    imshow(img)
    
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)
    
    print(happy_model.predict(x))
    

    测试结果

    [[ 0.]]
    

    1.5 - 其他一些有用的功能

    • model.summary():打印出你的每一层的大小细节
    • plot_model() : 绘制出布局图
    happy_model.summary()
    

    执行结果

    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    input_2 (InputLayer)         (None, 64, 64, 3)         0         
    _________________________________________________________________
    zero_padding2d_2 (ZeroPaddin (None, 70, 70, 3)         0         
    _________________________________________________________________
    conv0 (Conv2D)               (None, 64, 64, 32)        4736      
    _________________________________________________________________
    bn0 (BatchNormalization)     (None, 64, 64, 32)        128       
    _________________________________________________________________
    activation_2 (Activation)    (None, 64, 64, 32)        0         
    _________________________________________________________________
    max_pool (MaxPooling2D)      (None, 32, 32, 32)        0         
    _________________________________________________________________
    flatten_2 (Flatten)          (None, 32768)             0         
    _________________________________________________________________
    fc (Dense)                   (None, 1)                 32769     
    =================================================================
    Total params: 37,633
    Trainable params: 37,569
    Non-trainable params: 64
    _________________________________________________________________
    

    我们来绘制一下图:

    天坑

    1. 请下载并安装Graphviz的windows版本,然后写入环境变量,博主的环境变量填的是:E:\Anaconda3\Lib\site-packages\Graphviz\bin,因人而异吧.
    2. 请安装pydot-ng & graphviz,其代码CMD代码为:pip install pydot-ng & pip install graphviz或者是pip install pydotpip install graphviz
    3. 重启Jupyter Notebook 【手动微笑】【手动再见】
    %matplotlib inline
    plot_model(happy_model, to_file='happy_model.png')
    SVG(model_to_dot(happy_model).create(prog='dot', format='svg'))
    

    happy_model.png


    2 - 残差网络的搭建

      这里我们将学习怎样使用残差网络构建一个非常深的卷积网络。理论上越深的网络越能够实现越复杂的功能,但是在实际上却非常难以训练。**残差网络**就是为了解决深网络的难以训练的问题的。

    在本文章中,我们将:

    • 实现基本的残差块。
    • 将这些残差块放在一起,实现并训练用于图像分类的神经网络。

    本次实验将使用**Keras框架**

    在解决问题之前,我们先来导入库函数:

    import numpy as np
    import tensorflow as tf
    
    from keras import layers
    from keras.layers import Input, Add, Dense, Activation, ZeroPadding2D, BatchNormalization, Flatten, Conv2D, AveragePooling2D, MaxPooling2D, GlobalMaxPooling2D
    from keras.models import Model, load_model
    from keras.preprocessing import image
    from keras.utils import layer_utils
    from keras.utils.data_utils import get_file
    from keras.applications.imagenet_utils import preprocess_input
    from keras.utils.vis_utils import model_to_dot
    from keras.utils import plot_model
    from keras.initializers import glorot_uniform
    
    import pydot
    from IPython.display import SVG
    import scipy.misc
    from matplotlib.pyplot import imshow
    import keras.backend as K
    K.set_image_data_format('channels_last')
    K.set_learning_phase(1)
    
    import resnets_utils 
    

    2.1 - 深层网络的麻烦

      上周,我们构建了第一个卷积神经网络。最近几年,卷积神经网络变得越来越深,从只从几层(例如AlexNet)到超过一百层。

      使用深层网络最大的好处就是它能够完成很复杂的功能,它能够从边缘(浅层)到非常复杂的特征(深层)中不同的抽象层次的特征中学习。然而,使用比较深的网络通常没有什么好处,一个特别大的麻烦就在于训练的时候会产生梯度消失,非常深的网络通常会有一个梯度信号,该信号会迅速的消退,从而使得梯度下降变得非常缓慢。更具体的说,在梯度下降的过程中,当你从最后一层回到第一层的时候,你在每个步骤上乘以权重矩阵,因此梯度值可以迅速的指数式地减少到0(在极少数的情况下会迅速增长,造成梯度爆炸)。

      在训练的过程中,你可能会看到开始几层的梯度的大小(或范数)迅速下降到0,如下图:

    vanishing_grad_kiank.png

    **图 1** : **梯度消失**
    在前几层中随着迭代次数的增加,学习的速度会下降的非常快。

      为了解决这个问题,我们将构建残差网络。

    2.2 - 构建一个残差网络

      在残差网络中,一个“捷径(shortcut)”或者说“跳跃连接(skip connection)”允许梯度直接反向传播到更浅的层,如下图:
    skip_connection_kiank.png

    **图 2** : 残差网络中跳跃连接的残差块示意。

      图像左边是神经网络的主路,图像右边是添加了一条捷径的主路,通过这些残差块堆叠在一起,可以形成一个非常深的网络。

      我们在视频中可以看到使用捷径的方式使得每一个残差块能够很容易学习到恒等式功能,这意味着我们可以添加很多的残差块而不会损害训练集的表现。

      残差块有两种类型,主要取决于输入输出的维度是否相同,下面我们来看看吧~

    2.2.1 - 恒等块(Identity block)

      恒等块是残差网络使用的的标准块,对应于输入的激活值(比如 a [ l ] a^{[l]} a[l])与输出激活值(比如 a [ l + 1 ] a^{[l+1]} a[l+1])具有相同的维度。为了具象化残差块的不同步骤,我们来看看下面的图吧~
    idblock2_kiank.png

    **图 3** : **恒等块。** 使用的是跳跃连接,幅度为两层。

      上图中,上面的曲线路径是“捷径”,下面的直线路径是主路径。在上图中,我们依旧把CONV2D 与 ReLU包含到了每个步骤中,为了提升训练的速度,我们在每一步也把数据进行了归一化(BatchNorm),不要害怕这些东西,因为Keras框架已经实现了这些东西,调用BatchNorm只需要一行代码。

      在实践中,我们要做一个更强大的版本:跳跃连接会跳过3个隐藏层而不是两个,就像下图:
    idblock3_kiank.png

    **图 4** : **恒等块。** 使用的是跳跃连接,幅度为三层。

    每个步骤如下:

    1. 主路径的第一部分:

      • 第一个CONV2D有 F 1 F_1 F1个过滤器,其大小为( 1 1 1 1 1 1),步长为(1,1),使用填充方式为“valid”,命名规则为conv_name_base + '2a',使用 0 0 0作为随机种子为其初始化。

      • 第一个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2a'

      • 接着使用ReLU激活函数,它没有命名也没有超参数。

    2. 主路径的第二部分:

      • 第二个CONV2D有 F 2 F_2 F2个过滤器,其大小为( f f f f f f),步长为(1,1),使用填充方式为“same”,命名规则为conv_name_base + '2b',使用 0 0 0作为随机种子为其初始化。

      • 第二个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2b'

      • 接着使用ReLU激活函数,它没有命名也没有超参数。

    3. 主路径的第三部分:

      • 第三个CONV2D有 F 3 F_3 F3个过滤器,其大小为( 1 1 1 1 1 1),步长为(1,1),使用填充方式为“valid”,命名规则为conv_name_base + '2c',使用 0 0 0作为随机种子为其初始化。

      • 第三个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2c'

      • 注意这里没有ReLU函数

    4. 最后一步:

      • 将捷径与输入加在一起

      • 使用ReLU激活函数,它没有命名也没有超参数。

    接下来我们就要实现残差网络的恒等块了,请务必查看下面的中文手册:

    def identity_block(X, f, filters, stage, block):
        """
        实现图3的恒等块
        
        参数:
            X - 输入的tensor类型的数据,维度为( m, n_H_prev, n_W_prev, n_H_prev )
            f - 整数,指定主路径中间的CONV窗口的维度
            filters - 整数列表,定义了主路径每层的卷积层的过滤器数量
            stage - 整数,根据每层的位置来命名每一层,与block参数一起使用。
            block - 字符串,据每层的位置来命名每一层,与stage参数一起使用。
            
        返回:
            X - 恒等块的输出,tensor类型,维度为(n_H, n_W, n_C)
        
        """
        
        #定义命名规则
        conv_name_base = "res" + str(stage) + block + "_branch"
        bn_name_base   = "bn"  + str(stage) + block + "_branch"
        
        #获取过滤器
        F1, F2, F3 = filters
        
        #保存输入数据,将会用于为主路径添加捷径
        X_shortcut = X
        
        #主路径的第一部分
        ##卷积层
        X = Conv2D(filters=F1, kernel_size=(1,1), strides=(1,1) ,padding="valid",
                   name=conv_name_base+"2a", kernel_initializer=glorot_uniform(seed=0))(X)
        ##归一化
        X = BatchNormalization(axis=3,name=bn_name_base+"2a")(X)
        ##使用ReLU激活函数
        X = Activation("relu")(X)
        
        #主路径的第二部分
        ##卷积层
        X = Conv2D(filters=F2, kernel_size=(f,f),strides=(1,1), padding="same",
                   name=conv_name_base+"2b", kernel_initializer=glorot_uniform(seed=0))(X)
        ##归一化
        X = BatchNormalization(axis=3,name=bn_name_base+"2b")(X)
        ##使用ReLU激活函数
        X = Activation("relu")(X)
        
        
        #主路径的第三部分
        ##卷积层
        X = Conv2D(filters=F3, kernel_size=(1,1), strides=(1,1), padding="valid",
                   name=conv_name_base+"2c", kernel_initializer=glorot_uniform(seed=0))(X)
        ##归一化
        X = BatchNormalization(axis=3,name=bn_name_base+"2c")(X)
        ##没有ReLU激活函数
        
        #最后一步:
        ##将捷径与输入加在一起
        X = Add()([X,X_shortcut])
        ##使用ReLU激活函数
        X = Activation("relu")(X)
        
        return X
    

    我们来测试一下:

    tf.reset_default_graph()
    with tf.Session() as test:
        np.random.seed(1)
        A_prev = tf.placeholder("float",[3,4,4,6])
        X = np.random.randn(3,4,4,6)
        A = identity_block(A_prev,f=2,filters=[2,4,6],stage=1,block="a")
        
        test.run(tf.global_variables_initializer())
        out = test.run([A],feed_dict={A_prev:X,K.learning_phase():0})
        print("out = " + str(out[0][1][1][0]))
        
        test.close()
    

    测试结果

    out = [ 0.19716813  0.          1.35612273  2.17130733  0.          1.33249867]
    

    2.2.2 - 卷积块

      我们已经实现了残差网络的恒等块,现在,残差网络的卷积块是另一种类型的残差块,它适用于输入输出的维度不一致的情况,它不同于上面的恒等块,与之区别在于,捷径中有一个CONV2D层,如下图:
    convblock_kiank.png

    **图 5** : **卷积块**

      捷径中的卷积层将把输入 x x x卷积为不同的维度,因此在主路径最后那里需要适配捷径中的维度。比如:把激活值中的宽高减少2倍,我们可以使用1x1的卷积,步伐为2。捷径上的卷积层不使用任何非线性激活函数,它的主要作用是仅仅应用(学习后的)线性函数来减少输入的维度,以便在后面的加法步骤中的维度相匹配。

    具体步骤如下:

    1. 主路径第一部分:

      • 第一个卷积层有 F 1 F_1 F1个过滤器,其维度为( 1 1 1 1 1 1),步伐为( s s s s s s),使用“valid”的填充方式,命名规则为conv_name_base + '2a'

      • 第一个规范层是通道的轴归一化,其命名规则为bn_name_base + '2a'

      • 使用ReLU激活函数,它没有命名规则也没有超参数。

    2. 主路径第二部分:

      • 第二个卷积层有 F 2 F_2 F2个过滤器,其维度为( f f f f f f),步伐为( 1 1 1 1 1 1),使用“same”的填充方式,命名规则为conv_name_base + '2b'

      • 第二个规范层是通道的轴归一化,其命名规则为bn_name_base + '2b'

      • 使用ReLU激活函数,它没有命名规则也没有超参数。

    3. 主路径第三部分:

      • 第三个卷积层有 F 3 F_3 F3个过滤器,其维度为( 1 1 1 1 1 1),步伐为( s s s s s s),使用“valid”的填充方式,命名规则为conv_name_base + '2c'

      • 第三个规范层是通道的轴归一化,其命名规则为bn_name_base + '2c'

      • 没有激活函数

    4. 捷径:

      • 此卷积层有 F 3 F_3 F3个过滤器,其维度为( 1 1 1 1 1 1),步伐为( s s s s s s),使用“valid”的填充方式,命名规则为conv_name_base + '1'

      • 此规范层是通道的轴归一化,其命名规则为bn_name_base + '1'

    5. 最后一步:

      • 将捷径与输入加在一起

      • 使用ReLU激活函数

    我们要做的是实现卷积块,请务必查看下面的中文手册:

    def convolutional_block(X, f, filters, stage, block, s=2):
        """
        实现图5的卷积块
        
        参数:
            X - 输入的tensor类型的变量,维度为( m, n_H_prev, n_W_prev, n_C_prev)
            f - 整数,指定主路径中间的CONV窗口的维度
            filters - 整数列表,定义了主路径每层的卷积层的过滤器数量
            stage - 整数,根据每层的位置来命名每一层,与block参数一起使用。
            block - 字符串,据每层的位置来命名每一层,与stage参数一起使用。
            s - 整数,指定要使用的步幅
        
        返回:
            X - 卷积块的输出,tensor类型,维度为(n_H, n_W, n_C)
        """
        
        #定义命名规则
        conv_name_base = "res" + str(stage) + block + "_branch"
        bn_name_base   = "bn"  + str(stage) + block + "_branch"
        
        #获取过滤器数量
        F1, F2, F3 = filters
        
        #保存输入数据
        X_shortcut = X
        
        #主路径
        ##主路径第一部分
        X = Conv2D(filters=F1, kernel_size=(1,1), strides=(s,s), padding="valid",
                   name=conv_name_base+"2a", kernel_initializer=glorot_uniform(seed=0))(X)
        X = BatchNormalization(axis=3,name=bn_name_base+"2a")(X)
        X = Activation("relu")(X)
        
        ##主路径第二部分
        X = Conv2D(filters=F2, kernel_size=(f,f), strides=(1,1), padding="same",
                   name=conv_name_base+"2b", kernel_initializer=glorot_uniform(seed=0))(X)
        X = BatchNormalization(axis=3,name=bn_name_base+"2b")(X)
        X = Activation("relu")(X)
        
        ##主路径第三部分
        X = Conv2D(filters=F3, kernel_size=(1,1), strides=(1,1), padding="valid",
                   name=conv_name_base+"2c", kernel_initializer=glorot_uniform(seed=0))(X)
        X = BatchNormalization(axis=3,name=bn_name_base+"2c")(X)
        
        #捷径
        X_shortcut = Conv2D(filters=F3, kernel_size=(1,1), strides=(s,s), padding="valid",
                   name=conv_name_base+"1", kernel_initializer=glorot_uniform(seed=0))(X_shortcut)
        X_shortcut = BatchNormalization(axis=3,name=bn_name_base+"1")(X_shortcut)
        
        #最后一步
        X = Add()([X,X_shortcut])
        X = Activation("relu")(X)
        
        return X
    

    我们来测试一下:

    tf.reset_default_graph()
    
    with tf.Session() as test:
        np.random.seed(1)
        A_prev = tf.placeholder("float",[3,4,4,6])
        X = np.random.randn(3,4,4,6)
        
        A = convolutional_block(A_prev,f=2,filters=[2,4,6],stage=1,block="a")
        test.run(tf.global_variables_initializer())
        
        out = test.run([A],feed_dict={A_prev:X,K.learning_phase():0})
        print("out = " + str(out[0][1][1][0]))
        
        test.close()
    

    测试结果

    out = [ 0.09018463  1.23489773  0.46822017  0.0367176   0.          0.65516603]
    

    2.3 - 构建你的第一个残差网络(50层)

      我们已经做完所需要的所有残差块了,下面这个图就描述了神经网络的算法细节,图中的"ID BLOCK"是指标准的恒等块,"ID BLOCK X3"是指把三个恒等块放在一起。
    resnet_kiank.png

    **图 6** : **ResNet-50 model**

    这个50层的网络的细节如下:

    1. 对输入数据进行0填充,padding =(3,3)

    2. stage1:

      • 卷积层有64个过滤器,其维度为(7,7),步伐为(2,2),命名为“conv1”

      • 规范层(BatchNorm)对输入数据进行通道轴归一化。

      • 最大值池化层使用一个(3,3)的窗口和(2,2)的步伐。

    3. stage2:

      • 卷积块使用f=3个大小为[64,64,256]的过滤器,f=3,s=1,block=“a”

      • 2个恒等块使用三个大小为[64,64,256]的过滤器,f=3,block=“b”、“c”

    4. stage3:

      • 卷积块使用f=3个大小为[128,128,512]的过滤器,f=3,s=2,block=“a”

      • 3个恒等块使用三个大小为[128,128,512]的过滤器,f=3,block=“b”、“c”、“d”

    5. stage4:

      • 卷积块使用f=3个大小为[256,256,1024]的过滤器,f=3,s=2,block=“a”

      • 5个恒等块使用三个大小为[256,256,1024]的过滤器,f=3,block=“b”、“c”、“d”、“e”、“f”

    6. stage5:

      • 卷积块使用f=3个大小为[512,512,2048]的过滤器,f=3,s=2,block=“a”

      • 2个恒等块使用三个大小为[256,256,2048]的过滤器,f=3,block=“b”、“c”

    7. 均值池化层使用维度为(2,2)的窗口,命名为“avg_pool”

    8. 展开操作没有任何超参数以及命名

    9. 全连接层(密集连接)使用softmax激活函数,命名为"fc" + str(classes)

    为了实现这50层的残差网络,我们需要查看一下手册:

    def ResNet50(input_shape=(64,64,3),classes=6):
        """
        实现ResNet50
        CONV2D -> BATCHNORM -> RELU -> MAXPOOL -> CONVBLOCK -> IDBLOCK*2 -> CONVBLOCK -> IDBLOCK*3
        -> CONVBLOCK -> IDBLOCK*5 -> CONVBLOCK -> IDBLOCK*2 -> AVGPOOL -> TOPLAYER
        
        参数:
            input_shape - 图像数据集的维度
            classes - 整数,分类数
            
        返回:
            model - Keras框架的模型
            
        """
        
        #定义tensor类型的输入数据
        X_input = Input(input_shape)
        
        #0填充
        X = ZeroPadding2D((3,3))(X_input)
        
        #stage1
        X = Conv2D(filters=64, kernel_size=(7,7), strides=(2,2), name="conv1",
                   kernel_initializer=glorot_uniform(seed=0))(X)
        X = BatchNormalization(axis=3, name="bn_conv1")(X)
        X = Activation("relu")(X)
        X = MaxPooling2D(pool_size=(3,3), strides=(2,2))(X)
        
        #stage2
        X = convolutional_block(X, f=3, filters=[64,64,256], stage=2, block="a", s=1)
        X = identity_block(X, f=3, filters=[64,64,256], stage=2, block="b")
        X = identity_block(X, f=3, filters=[64,64,256], stage=2, block="c")
        
        #stage3
        X = convolutional_block(X, f=3, filters=[128,128,512], stage=3, block="a", s=2)
        X = identity_block(X, f=3, filters=[128,128,512], stage=3, block="b")
        X = identity_block(X, f=3, filters=[128,128,512], stage=3, block="c")
        X = identity_block(X, f=3, filters=[128,128,512], stage=3, block="d")
        
        #stage4
        X = convolutional_block(X, f=3, filters=[256,256,1024], stage=4, block="a", s=2)
        X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="b")
        X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="c")
        X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="d")
        X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="e")
        X = identity_block(X, f=3, filters=[256,256,1024], stage=4, block="f")
        
        #stage5
        X = convolutional_block(X, f=3, filters=[512,512,2048], stage=5, block="a", s=2)
        X = identity_block(X, f=3, filters=[512,512,2048], stage=5, block="b")
        X = identity_block(X, f=3, filters=[512,512,2048], stage=5, block="c")
        
        #均值池化层
        X = AveragePooling2D(pool_size=(2,2),padding="same")(X)
        
        #输出层
        X = Flatten()(X)
        X = Dense(classes, activation="softmax", name="fc"+str(classes),
                  kernel_initializer=glorot_uniform(seed=0))(X)
        
        
        #创建模型
        model = Model(inputs=X_input, outputs=X, name="ResNet50")
        
        return model
    

    然后我们对模型做实体化和编译工作:

    model = ResNet50(input_shape=(64,64,3),classes=6)
    model.compile(optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"])
    
    

    现在模型已经准备好了,接下来就是加载训练集进行训练。
    signs_data_kiank.png

    **图 7** : **手势数据集**
    X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = resnets_utils.load_dataset()
    
    # Normalize image vectors
    X_train = X_train_orig / 255.
    X_test = X_test_orig / 255.
    
    # Convert training and test labels to one hot matrices
    Y_train = resnets_utils.convert_to_one_hot(Y_train_orig, 6).T
    Y_test = resnets_utils.convert_to_one_hot(Y_test_orig, 6).T
    
    print("number of training examples = " + str(X_train.shape[0]))
    print("number of test examples = " + str(X_test.shape[0]))
    print("X_train shape: " + str(X_train.shape))
    print("Y_train shape: " + str(Y_train.shape))
    print("X_test shape: " + str(X_test.shape))
    print("Y_test shape: " + str(Y_test.shape))
    

    执行结果

    number of training examples = 1080
    number of test examples = 120
    X_train shape: (1080, 64, 64, 3)
    Y_train shape: (1080, 6)
    X_test shape: (120, 64, 64, 3)
    Y_test shape: (120, 6)
    

    运行模型两代,batch=32,每代大约3分钟左右。

    model.fit(X_train,Y_train,epochs=2,batch_size=32)
    

    执行结果

    Epoch 1/2
    1080/1080 [==============================] - 200s 185ms/step - loss: 3.0667 - acc: 0.2593
    Epoch 2/2
    1080/1080 [==============================] - 186s 172ms/step - loss: 1.9755 - acc: 0.4093
    
    • 1 2 \frac{1}{2} 21Epoch中,loss在15之间算正常,acc在0.20.5之间算正常,你的结果和我的不一样也算正常。
    • 2 2 \frac{2}{2} 22Epoch中,loss在15之间算正常,acc在0.20.5之间算正常,你可以看到损失在下降,准确率在上升。

    我们来评估一下模型:

    preds = model.evaluate(X_test,Y_test)
    
    print("误差值 = " + str(preds[0]))
    print("准确率 = " + str(preds[1]))
    
    

    执行结果

    120/120 [==============================] - 5s 44ms/step
    误差值 = 12.3403865178
    准确率 = 0.175000000497
    

      在完成这个任务之后,如果愿意的话,您还可以选择继续训练RESNET。当我们训练20代时,我们得到了更好的性能,但是在得在CPU上训练需要一个多小时。使用GPU的话,博主已经在手势数据集上训练了自己的RESNET50模型的权重,你可以使用下面的代码载并运行博主的训练模型,加载模型可能需要1min。

    #加载模型
    model = load_model("ResNet50.h5") 
    

    然后测试一下博主训练出来的权值:

    preds = model.evaluate(X_test,Y_test)
    print("误差值 = " + str(preds[0]))
    print("准确率 = " + str(preds[1]))
    

    测试结果

    120/120 [==============================] - 4s 35ms/step
    误差值 = 0.108543064694
    准确率 = 0.966666662693
    

    2.4 使用自己的图片做测试

    按理来说,训练数据集与自己的数据集是不一样的,但是我们也可以来试试嘛。

    from PIL import Image
    import numpy as np
    import matplotlib.pyplot as plt # plt 用于显示图片
    
    %matplotlib inline
    
    img_path = 'images/fingers_big/2.jpg'
    
    my_image = image.load_img(img_path, target_size=(64, 64))
    my_image = image.img_to_array(my_image)
    
    my_image = np.expand_dims(my_image,axis=0)
    my_image = preprocess_input(my_image)
    
    print("my_image.shape = " + str(my_image.shape))
    
    print("class prediction vector [p(0), p(1), p(2), p(3), p(4), p(5)] = ")
    print(model.predict(my_image))
    
    my_image = scipy.misc.imread(img_path)
    plt.imshow(my_image)
    

    执行结果

    my_image.shape = (1, 64, 64, 3)
    class prediction vector [p(0), p(1), p(2), p(3), p(4), p(5)] = 
    [[ 1.  0.  0.  0.  0.  0.]]
    

    请忽略博主这臃肿的手【手动捂脸】

    我们可以看一下网络的节点的大小细节:

    model.summary()
    

    执行结果

    __________________________________________________________________________________________________
    Layer (type)                    Output Shape         Param #     Connected to                     
    ==================================================================================================
    input_1 (InputLayer)            (None, 64, 64, 3)    0                                            
    __________________________________________________________________________________________________
    zero_padding2d_1 (ZeroPadding2D (None, 70, 70, 3)    0           input_1[0][0]                    
    __________________________________________________________________________________________________
    conv1 (Conv2D)                  (None, 32, 32, 64)   9472        zero_padding2d_1[0][0]           
    __________________________________________________________________________________________________
    bn_conv1 (BatchNormalization)   (None, 32, 32, 64)   256         conv1[0][0]                      
    __________________________________________________________________________________________________
    activation_1 (Activation)       (None, 32, 32, 64)   0           bn_conv1[0][0]                   
    __________________________________________________________________________________________________
    max_pooling2d_1 (MaxPooling2D)  (None, 15, 15, 64)   0           activation_1[0][0]               
    __________________________________________________________________________________________________
    res2a_branch2a (Conv2D)         (None, 15, 15, 64)   4160        max_pooling2d_1[0][0]            
    __________________________________________________________________________________________________
    bn2a_branch2a (BatchNormalizati (None, 15, 15, 64)   256         res2a_branch2a[0][0]             
    __________________________________________________________________________________________________
    activation_2 (Activation)       (None, 15, 15, 64)   0           bn2a_branch2a[0][0]              
    __________________________________________________________________________________________________
    res2a_branch2b (Conv2D)         (None, 15, 15, 64)   36928       activation_2[0][0]               
    __________________________________________________________________________________________________
    bn2a_branch2b (BatchNormalizati (None, 15, 15, 64)   256         res2a_branch2b[0][0]             
    __________________________________________________________________________________________________
    activation_3 (Activation)       (None, 15, 15, 64)   0           bn2a_branch2b[0][0]              
    __________________________________________________________________________________________________
    res2a_branch2c (Conv2D)         (None, 15, 15, 256)  16640       activation_3[0][0]               
    __________________________________________________________________________________________________
    res2a_branch1 (Conv2D)          (None, 15, 15, 256)  16640       max_pooling2d_1[0][0]            
    __________________________________________________________________________________________________
    bn2a_branch2c (BatchNormalizati (None, 15, 15, 256)  1024        res2a_branch2c[0][0]             
    __________________________________________________________________________________________________
    bn2a_branch1 (BatchNormalizatio (None, 15, 15, 256)  1024        res2a_branch1[0][0]              
    __________________________________________________________________________________________________
    add_1 (Add)                     (None, 15, 15, 256)  0           bn2a_branch2c[0][0]              
                                                                     bn2a_branch1[0][0]               
    ······我就不放完啦~         
    __________________________________________________________________________________________________
    activation_49 (Activation)      (None, 2, 2, 2048)   0           add_16[0][0]                     
    __________________________________________________________________________________________________
    average_pooling2d_1 (AveragePoo (None, 1, 1, 2048)   0           activation_49[0][0]              
    __________________________________________________________________________________________________
    flatten_1 (Flatten)             (None, 2048)         0           average_pooling2d_1[0][0]        
    __________________________________________________________________________________________________
    fc6 (Dense)                     (None, 6)            12294       flatten_1[0][0]                  
    ==================================================================================================
    Total params: 23,600,006
    Trainable params: 23,546,886
    Non-trainable params: 53,120
    

    我们来看一下绘制的结构图~

    plot_model(model, to_file='model.png')
    SVG(model_to_dot(model).create(prog='dot', format='svg'))
    

    执行结果长图预警
    model


    3 - 相关库代码

    3.1 - kt_utils.py

    #kt_utils.py
    
    import keras.backend as K
    import math
    import numpy as np
    import h5py
    import matplotlib.pyplot as plt
    
    
    def mean_pred(y_true, y_pred):
        return K.mean(y_pred)
    
    def load_dataset():
        train_dataset = h5py.File('datasets/train_happy.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_happy.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
    

    3.2 - resnets_utils.py

    #resnets_utils.py
    
    import os
    import numpy as np
    import tensorflow as tf
    import h5py
    import math
    
    def load_dataset():
        train_dataset = h5py.File('datasets/train_signs.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_signs.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
    
    
    def random_mini_batches(X, Y, mini_batch_size = 64, seed = 0):
        """
        Creates a list of random minibatches from (X, Y)
        
        Arguments:
        X -- input data, of shape (input size, number of examples) (m, Hi, Wi, Ci)
        Y -- true "label" vector (containing 0 if cat, 1 if non-cat), of shape (1, number of examples) (m, n_y)
        mini_batch_size - size of the mini-batches, integer
        seed -- this is only for the purpose of grading, so that you're "random minibatches are the same as ours.
        
        Returns:
        mini_batches -- list of synchronous (mini_batch_X, mini_batch_Y)
        """
        
        m = X.shape[0]                  # number of training examples
        mini_batches = []
        np.random.seed(seed)
        
        # Step 1: Shuffle (X, Y)
        permutation = list(np.random.permutation(m))
        shuffled_X = X[permutation,:,:,:]
        shuffled_Y = Y[permutation,:]
    
        # Step 2: Partition (shuffled_X, shuffled_Y). Minus the end case.
        num_complete_minibatches = math.floor(m/mini_batch_size) # number of mini batches of size mini_batch_size in your partitionning
        for k in range(0, num_complete_minibatches):
            mini_batch_X = shuffled_X[k * mini_batch_size : k * mini_batch_size + mini_batch_size,:,:,:]
            mini_batch_Y = shuffled_Y[k * mini_batch_size : k * mini_batch_size + mini_batch_size,:]
            mini_batch = (mini_batch_X, mini_batch_Y)
            mini_batches.append(mini_batch)
        
        # Handling the end case (last mini-batch < mini_batch_size)
        if m % mini_batch_size != 0:
            mini_batch_X = shuffled_X[num_complete_minibatches * mini_batch_size : m,:,:,:]
            mini_batch_Y = shuffled_Y[num_complete_minibatches * mini_batch_size : m,:]
            mini_batch = (mini_batch_X, mini_batch_Y)
            mini_batches.append(mini_batch)
        
        return mini_batches
    
    
    def convert_to_one_hot(Y, C):
        Y = np.eye(C)[Y.reshape(-1)].T
        return Y
    
    
    def forward_propagation_for_predict(X, parameters):
        """
        Implements the forward propagation for the model: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SOFTMAX
        
        Arguments:
        X -- input dataset placeholder, of shape (input size, number of examples)
        parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2", "W3", "b3"
                      the shapes are given in initialize_parameters
        Returns:
        Z3 -- the output of the last LINEAR unit
        """
        
        # Retrieve the parameters from the dictionary "parameters" 
        W1 = parameters['W1']
        b1 = parameters['b1']
        W2 = parameters['W2']
        b2 = parameters['b2']
        W3 = parameters['W3']
        b3 = parameters['b3'] 
                                                               # Numpy Equivalents:
        Z1 = tf.add(tf.matmul(W1, X), b1)                      # Z1 = np.dot(W1, X) + b1
        A1 = tf.nn.relu(Z1)                                    # A1 = relu(Z1)
        Z2 = tf.add(tf.matmul(W2, A1), b2)                     # Z2 = np.dot(W2, a1) + b2
        A2 = tf.nn.relu(Z2)                                    # A2 = relu(Z2)
        Z3 = tf.add(tf.matmul(W3, A2), b3)                     # Z3 = np.dot(W3,Z2) + b3
        
        return Z3
    
    def predict(X, parameters):
        
        W1 = tf.convert_to_tensor(parameters["W1"])
        b1 = tf.convert_to_tensor(parameters["b1"])
        W2 = tf.convert_to_tensor(parameters["W2"])
        b2 = tf.convert_to_tensor(parameters["b2"])
        W3 = tf.convert_to_tensor(parameters["W3"])
        b3 = tf.convert_to_tensor(parameters["b3"])
        
        params = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2,
                  "W3": W3,
                  "b3": b3}
        
        x = tf.placeholder("float", [12288, 1])
        
        z3 = forward_propagation_for_predict(x, params)
        p = tf.argmax(z3)
        
        sess = tf.Session()
        prediction = sess.run(p, feed_dict = {x: X})
            
        return prediction
    
    展开全文
  • 吴恩达课后编程作业】第二周作业 - Logistic回归-识别猫的图片资源
  • 吴恩达 深度学习 编程作业(4-4)- Face Recognition for the Happy House & Art: Neural Style Transfer-附件资源
  • 吴恩达课后编程作业】第三周作业 (附答案、代码)隐藏层神经网络 神经网络和深度学习 上一篇:【课程1 - 第二周作业】 ✌✌✌✌ 【目录】 ✌✌✌✌ 下一篇:【课程1 - 第三周作业】 首先说明一下,大学生一枚,...
  • 我是一名小白,最近学习pytorch,用pytorch复现一下课后编程作业 一、 导入库 开始之前先导入库 import numpy as np import h5py import matplotlib.pyplot as plt import tf_utils import time import ...
  • 吴恩达老师的深度学习课程的第四部分——卷积神经网络的课后作业,四周作业(包括课后的quiz和编程作业)都在里面,是搬运别人的资源,自己做了,感觉很不错。当时找资源时,各种痛苦,也受到了好心人的帮助,没有...
  • 在开始之前,首先声明本文是作为一个初学者的学习笔记,在学习CSDN博主「何宽」的原创文章,原文链接:https://blog.csdn.net/u013733326/article/details/79702148 【【吴恩达课后编程作业】Course 1 - 神经网络和...
  • 所用到的文件 ex2.m-Octave/MATLAB脚本,该脚本引导您完成ex2 reg.m-Octave/MATLAB脚本,用于ex2data1.txt练习的后面部分- Ex2data1.txt练习的前半部分的训练集- ex2data2.txt练习的后半部分的训练集提交。...
  • 机器学习所有课后作业 大大的 只看没用 做了练习题才有点儿用 课后答案可以网上找 练习题目很有必要
  • 【中文】【吴恩达课后编程作业】Course 2 - 改善深层神经网络 - 第一周作业(1&amp;2&amp;3) - 初始化、正则化、梯度校验 上一篇:【课程2 - 第一周测验】※※※※※ 【回到目录】※※※※※下一篇:...
  • 吴恩达课后编程作业】第二周 - PA1 - 具有神经网络思维的Logistic回归【吴恩达课后编程作业】第二周 - PA1 - 具有神经网络思维的Logistic回归
  • 吴恩达课后编程作业)Course 1 - 神经网络和深度学习 - 第二周作业环境准备下载资料安装库显示图片查看数据集情况数据维度转换标准化数据定义sigmoid函数初始化参数w和b了计算成本函数优化函数预测函数整合到model...
  • 【中文】【吴恩达课后编程作业】Course 4 - 卷积神经网络 - 第一周作业 - 搭建卷积神经网络模型以及应用(1&amp;2) 上一篇:【课程4 - 第一周测验】※※※※※ 【回到目录】※※※※※下一篇:【课程4 -...
  • Coursera 机器学习 吴恩达 课后习题答案 编程练习答案
  • 吴恩达课后编程作业第二周

    千次阅读 2019-01-18 21:31:48
    ,我基于他的文章加以自己的理解发表这篇博客,力求让大家以最轻松的姿态理解吴恩达的视频,如有不妥的地方欢迎大家指正。 本文所使用的资料已上传到百度网盘 【点击下载】 ,请在开始之前下载好所需资料,然后...
  • 原博客中作者用的是tf1.x版本的,本文用的是tf2.x版本 1 - Keras 入门 - 笑脸识别 本次我们将: 学习到一个高级的神经网络的框架,能够运行在包括TensorFlow和CNTK的几个较低级别的框架之上的框架。...
  • 【超详细】【吴恩达课后作业】Course 1-神经网络与深度学习-第四周编程作业-附件资源
  • 吴恩达卷积神经网络,第一周作业PyTorch版本代码(gpu-cpu通用) 1.PyCharm上运行的PyTorch项目 2.基础的卷积神经网络搭建 3.加入了gpu加速所需的代码 4.含数据集+cnn_utils.py【对原版本做了简化】 5.含训练、...
  • 吴恩达ex1编程作业

    2018-05-08 20:54:32
    course吴恩达ex1机器学习编程作业答案,自己写的仅供参考

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,516
精华内容 606
关键字:

吴恩达课后编程作业