吴恩达第二周作业 - 具有神经网络思维的Logistic回归

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36022260/article/details/83339618

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

  做一个识别猫的神经网络,希望可以动手一步一步实现。

导入所需的库:

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

可以查看lr_utils.py文件中的内容。

f = open('lr_utils.py','r')
print(f.read())

内容如下:

import numpy as np
import h5py
    
    
def load_dataset():
    train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")
    train_set_x_orig = np.array(train_dataset["train_set_x"][:]) # your train set features
    train_set_y_orig = np.array(train_dataset["train_set_y"][:]) # your train set labels

    test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")
    test_set_x_orig = np.array(test_dataset["test_set_x"][:]) # your test set features
    test_set_y_orig = np.array(test_dataset["test_set_y"][:]) # your test set labels

    classes = np.array(test_dataset["list_classes"][:]) # the list of classes
    
    train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
    test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
    
    return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes

train_set_x_orig :保存的是训练集里面的图像数据(本训练集有209张64x64的图像)。
train_set_y_orig :保存的是训练集的图像对应的分类值(【0 | 1】,0表示不是猫,1表示是猫)。
test_set_x_orig :保存的是测试集里面的图像数据(本训练集有50张64x64的图像)。
test_set_y_orig : 保存的是测试集的图像对应的分类值(【0 | 1】,0表示不是猫,1表示是猫)。
classes : 保存的是以bytes类型保存的两个字符串数据,数据为:[b’non-cat’ b’cat’]。

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

index = 25
plt.imshow(train_set_x_orig[index])

你也可以查看一下训练集的标签是什么样子的

[[0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0
  0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 1 0 0 1
  0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 1 1 0 0 1 0 0 0 0 1 0 1 0 1 1
  1 1 1 1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0
  1 1 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 0 0 1 1 0 1 0 1
  0 0 0 0 0 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0]]

我们可以结合训练集查看我们加载的东西。

print("y = " + str(train_set_y[:,index]) + ",it 's a " + classes[np.squeeze(train_set_y[:,index])].decode("utf-8") + " picture")

输出如下:

y = [1],it 's a cat picture
  • m_train :训练集里图片的数量。
  • m_test :测试集里图片的数量。
  • num_px : 训练、测试集里面的图片的宽度和高度(均为64x64)。
m_train = train_set_y.shape[1]
m_test  = test_set_y.shape[1]
num_px = train_set_x_orig.shape[1]
print('训练集图片数量m_train = '+str(m_train))
print('测试集图片数量m_test = '+str(m_test))
print('图片的大小:'+str(num_px)+','+str(num_px))
print('训练集图片维数:'+str(train_set_x_orig.shape))
print('训练集标签维数:'+str(train_set_y.shape))

输出如下:

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

为了方便,我们要把维度为(64,64,3)的numpy数组重新构造为(64 x 64 x 3,1)的数组,要乘以3的原因是每张图片是由64x64像素构成的,而每个像素点由(R,G,B)三原色构成的,所以要乘以3。在此之后,我们的训练和测试数据集是一个numpy数组,【每列代表一个平坦的图像】 ,应该有m_train和m_test列。

当你想将形状(a,b,c,d)的矩阵X平铺成形状(b * c * d,a)的矩阵X_flatten时,可以使用以下代码:

细节扩展:

矩阵(a,b,c,d)采取从右往左读法,例如(1,2,3,4)首先为(3,4)矩阵为一整体,然后(2,(3,4))矩阵当一整体,最后为(1,(2,(3,4)))这样就更容易理解矩阵的形状啦。

代码如下:

x = np.random.randn(1,2,3,4)
print(x)
print('-'*60)
print(x.shape[0])
x_f = x.reshape(x.shape[0],-1)
print(x_f.shape)
x_f = x.reshape(x.shape[0],-1).T
print(x_f.shape)
print(x_f)])

输出如下:

[[[[-1.5019957   0.27460599 -0.13503757  0.61359487]
   [ 0.43779331  1.8275767  -1.35215534  0.37215807]
   [ 1.20063894  1.57175417 -1.68714452 -2.98496014]]

  [[-1.42841035 -0.36910962 -0.05694828  1.48283783]
   [ 1.52399195 -0.69201781  0.15564753  0.32705825]
   [ 0.38743142  0.63207278  0.20439947 -0.29619398]]]]
------------------------------------------------------------
1
(1, 24)
(24, 1)
[[-1.5019957 ]
 [ 0.27460599]
 [-0.13503757]
 [ 0.61359487]
 [ 0.43779331]
 [ 1.8275767 ]
 [-1.35215534]
 [ 0.37215807]
 [ 1.20063894]
 [ 1.57175417]
 [-1.68714452]
 [-2.98496014]
 [-1.42841035]
 [-0.36910962]
 [-0.05694828]
 [ 1.48283783]
 [ 1.52399195]
 [-0.69201781]
 [ 0.15564753]
 [ 0.32705825]
 [ 0.38743142]
 [ 0.63207278]
 [ 0.20439947]
 [-0.29619398]]

回归正文:

#将训练集的维度降低并转置。
train_set_x_orig_f = train_set_x_orig.reshape(train_set_x_orig.shape[0],-1).T
#将测试集的维度降低并转置。
test_set_x_orig_f = test_set_x_orig.reshape(test_set_x_orig.shape[0],-1).T
print("训练集的维度降低并转置后的维度:"+str(train_set_x_orig_f.shape))
print("测试集的维度降低并转置后的维度:"+str(test_set_x_orig_f.shape))

输出如下:

训练集的维度降低并转置后的维度:(12288, 209)
测试集的维度降低并转置后的维度:(12288, 50)



建立神经网络的主要步骤是: 
1. 定义模型结构(例如输入特征的数量) 
2. 初始化模型的参数 
3. 循环:
 

  1. 计算当前损失(正向传播)
  2. 计算当前梯度(反向传播)
  3. 更新参数(梯度下降)

现在构建sigmoid()函数,需要使用sigmoid(w^T x +b)来实现。

def sigmoid(z):
    """
    z 为任意大小的标量或者numpy 数组
    """
    s = 1 / (1+ np.exp(-z))
    return s

测试:

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

输出:

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

初始化w,b

def initial_wb_zero(dim):
    """
    此函数为w初始化维度为dim,1)的 0 向量,并将b 初始化为0
    返回:
    初始化的 w  and   b
    
    """
    w = np.zeros(shape = (dim,1))
    b = 0
    assert (w.shape == (dim,1))
    assert (isinstance(b,float) or isinstance(b,int))
    
    return (w,b)
    

计算成本和渐变函数的实现:

def propagate(w,b,X,Y):
    """
    实现前向和后向的传播的成本函数和梯度。
    参数:
        w - 权重,大小不等的数组(num_px * num_px * 3,1)
        b - 偏差,一个标量
        X - 矩阵类型(num_px * num_px * 3,训练数量)
        Y - 标签矢量(不是猫为0,是猫为1),维度(1,训练集数量)
    返回:
        cost - 逻辑回归的负对数似然成本。
        dw   - 相对于w损失梯度,因此与w相同的形状
        db   - 相对于b损失梯度,因此与b相同的形状
        
    """
    m = X.shape[1] 
    # 正向传播
    A = sigmoid(np.dot(w.T,X) + b) #计算激活值
    cost = (-1/m) * np.sum(Y * np.log(A) + (1-Y)*(np.log(1-A))) #计算成本
    
    # 反向传播
    dw = (1/m)*np.dot(X,(A-Y).T)
    db = (1/m)*np.sum(A-Y)
    
    # 确认数据
    assert (dw.shape == w.shape)
    assert (db.dtype == float)
    cost = np.squeeze(cost)
    assert (cost.shape == ())
    
    grads = {"dw":dw , "db":db}
    
    return (grads,cost)

测试:

w ,b ,X ,Y = np.array([[1],[2]]) , 2 ,np.array([[1,2],[3,4]]),np.array([[1,0]])
grads , cost = propagate(w,b,X,Y)
print("dw="+str(grads["dw"]))
print("db="+str(grads["db"]))
print("cost="+str(cost))

输出:

dw=[[0.99993216]
 [1.99980262]]
db=0.49993523062470574
cost=6.000064773192205

目标是通过最小化成本函数 J 来学习 w。对于参数 lr ,更新规则是 θ=θ− lr dθ,其中 lr 是学习率。

def optimize(w,b,X,Y,max_iter,lr,out_cost = False):
    """
    此函数是用来优化w and b
    参数:
        w - 权重,大小不等的数组(num_px * num_px * 3,1)
        b - 偏差,一个标量
        X - 矩阵类型(num_px * num_px * 3,训练数量)
        Y - 标签矢量(不是猫为0,是猫为1),维度(1,训练集数量)
        max_iter - 迭代次数
        lr - 学习率
        out_cost - 打印损失值
    返回:
       params - 包含w and b的字典
       grads  - 包含权重和偏差相对于成本函数的梯度的字典
       costs  - 优化期间的成本列表
    """
    costs = []
    for i in range(max_iter):
        grads , cost = propagate(w,b,X,Y)
        dw , db = grads['dw'] , grads['db']
        w = w - lr*dw
        b = b - lr*db
            
        # 记录成本
        if i % 100 ==0:
            costs.append(cost)
#         if (out_cost) and (i%100==0):
#             print('iter number: '+str(i) + ', deviation: '+str(cost))
    
    params = { "w": w , "b" : b }
    grads = {"dw": dw  , "db" : db }
    
    return (params,grads,costs)

测试:

w ,b ,X ,Y = np.array([[1],[2]]) , 2 ,np.array([[1,2],[3,4]]),np.array([[1,0]])
params , grads , costs = optimize(w,b,X,Y,max_iter=100,lr=0.01,out_cost=False)
print("w="+str(params["w"]))
print("b="+str(params["b"]))
print("dw="+str(grads["dw"]))
print("db="+str(grads["db"]))

输出:

w=[[0.02729685]
 [0.06634584]]
b=1.5195244996409367
dw=[[0.80147827]
 [1.53224345]]
db=0.36538259061840267

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

现在我们要实现预测函数predict()。

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

def predict(w,b,X):
    """
    此函数用于预测标签是0 or 1
    
    返回:
        pred_nums - 包含所有图片的的所有预测 0 | 1 的值
    """
    m = X.shape[1]
    pred_nums = np.zeros((1,m))
    w = w.reshape(X.shape[0],1)
    # 计算预测猫在图片中出现的概率
    A = sigmoid(np.dot(w.T,X)+b)
    
    for i in range(A.shape[1]):
        pred_nums[0,i] = 1 if A[0,i] > 0.5 else 0
    assert (pred_nums.shape == (1,m))
    
    return pred_nums       

测试:

w ,b ,X ,Y = np.array([[1],[2]]) , 2 ,np.array([[1,2],[3,4]]),np.array([[1,0]])
pred_nums = predict(w,b,X)
print(pred_nums)

输出:

[[1. 1.]]

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

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

def model(x_train,y_train,x_test,y_test,max_iter=2000,lr = 0.5,out_cost=False):
    """
    参数:
        x_train - (num_px*num_px*3,m_train)训练集
        y_train - (1,m_train)训练标签集
        x_test  - (num_px*num_px*3,m_test)测试集
        y_test  - (1,m_test)测试标签集
        max_iter -优化迭代次数的超参数
        lr      - 学习率
        out_cost - 为True打印成本
    返回:
        dictor - 有关模型的字典
    """
    w , b = initial_wb_zero(x_train.shape[0])
    params , grads ,costs = optimize(w,b,x_train,y_train,max_iter,lr,out_cost)
    w , b = params["w"] , params["b"]
    predict_test = predict(w,b,x_test)
    predict_train = predict(w,b,x_train)
    print("Test Accuracy {}".format(100-np.mean(np.abs(predict_test-y_test))*100),'%')
    print("Train Accuracy {}".format(100-np.mean(np.abs(predict_train-y_train))*100),'%')
    dictor = { "costs": costs , 
               "predict_test": predict_test ,
              "predict_train": predict_train , 
              "w": w,
              "b": b,
              "max_iter": max_iter , 
              "lr": lr}
#     costs = np.squeeze(dictor['costs'])
#     plt.plot(costs)
#     plt.ylabel('costs')
#     plt.xlabel('itertions')
#     plt.title("Learning rate = "+str(lr))
#     plt.show()  
    return dictor
# dictor = model(train_set_x,train_set_y,test_set_x,test_set_y,max_iter=2000,lr=0.005,out_cost=True)
 
lrs = [0.005,0.01,0.001]
models ={}
for i in  lrs:
    print("Learning rate: "+str(i))
    models[str(i)] = model(train_set_x,train_set_y,test_set_x,test_set_y,max_iter=2000,lr= i,out_cost=True)
    print('\n'+'-'*40+'\n')
for i in lrs:
    plt.plot(np.squeeze(models[str(i)]['costs']))
plt.ylabel('costs')
plt.xlabel('iterations')

legend = plt.legend(loc ='upper center',shadow=True)
frame = legend.get_frame()
frame.set_facecolor('0.90')
plt.show()

输出:


Learning rate: 0.005
Test Accuracy 70.0 %
Train Accuracy 99.04306220095694 %

----------------------------------------

Learning rate: 0.01
Test Accuracy 70.0 %
Train Accuracy 99.52153110047847 %

----------------------------------------

Learning rate: 0.001





No handles with labels found to put in legend.





Test Accuracy 68.0 %
Train Accuracy 91.38755980861244 %


到这里逻辑回归就完整的实现了,不知道大家有没有看懂,欢迎留言!

注:以上代码全部在Jupyter notebook执行的,我的上传资源里会把代码文件上传,有需要的下载即可。



本文参考:https://blog.csdn.net/u013733326/article/details/79639509 
 

猜你喜欢

转载自blog.csdn.net/qq_36022260/article/details/83339618