开篇
学习了吴恩达神经网络与深度学习第一部分的课程后,赶紧做一做课后习题巩固一下。于是把第二周和第三周的课后编程练习跟着相关资料一步一步的理解与实现。对逻辑回归和单隐层神经网络有了逐渐深入的理解。
下面简单做下记录,其中最重要的一点是理解了为什么逻辑回归当数据集不是线性可分的时候分类效果会很差。
Week2_LogisticRegression_summary
Logistic 回归没有隐层,直接对输入层赋权重加和后作为激活函数的参数输出预测值。
总体思想为:
以向量化的形式同时对训练集每一幅图片进行操作,此操作是对一幅图片的所有像素,以RGB值为输入,
- 使用初始设置的全0权重W ( logistic初始设置没有不能全是0的限制,带隐层的神经网络则有此限制 ) 得到第一次的预测值A1;
- 与训练集里的对应标签(0 / 1)做logistic回归的成本函数计算;
- 根据梯度下降法迭代下一次计算,得到更新的W和b值,再次进行成本函数计算,直到成本函数接近于0时意味着训练的差不多了;
- 得到训练完以后的W和b值,用测试集来检验性能。
其实,“Logistic回归”虽然名字叫“回归”,但其实还是用于分类(0或1)。特点是:
- 没有隐藏层
- 激活函数:sigmoid
- 损失函数 :
- 梯度下降法
Week3_OneHiddenLayerNN_summary
本周研究的是一个通过含有单隐层的神经网络完成的平面数据分类。自己跟着教程一步一步敲得代码,感觉比粘贴复制更容易发现细节问题,比如python语法、库函数调用方法以及对整个思路的理解。
这次的数据集包含X, Y两个矩阵,X是数据二维坐标值,X.shape = (2, 400),Y是每个坐标值的标签(0或1),Y.shape = (1, 400)。通过plt.scatter()函数 # plt.scatter(X[0, :], X[1, :], c=np.squeeze(Y), s=40, cmap=plt.cm.Spectral)
绘制其散点图如下所示:
这次要做的分类是对平面区域的分类。本质是通过对训练集位置(x,y)的输入,通过单隐层神经网络输出预测值与标签进行比较,然后通过梯度下降法迭代不断训练,最终得到使成本函数最小的W和b值。再用测试集检测此模型得出该模型性能。注意:此任务使用logistic回归算法得到的准确率最高在大约47%,不能满足要求,如下图所示:
准确性只有47%的原因是数据集不是线性可分的,所以逻辑回归表现不佳。构建单隐层神经网络,其中激活函数隐藏层选的是tanh(),输出层选的是sigmoid()函数。输入层为二维坐标,输出值为0或者1。
当时,画图这个plt.contourf()这个函数还是不怎么明白,据说是画等高线?这块明白了才能明白为什么能画出这种非线性的划分。否则还是不明白他跟logistic回归的区别,因为,同样是(0,1)分类问题。
后来参考了两篇文章后,才恍然大悟:
- plt.contourf()函数的解释:等高线图plt.contourf()的使用方法
- 逻辑回归对非线性可分数据集的性能较差原因:如何高效判断数据是否可分
首先是plt.contourf()的参数问题。plt.contourf(x, y, z,[levels], **kwargs)中的z是高度值,其实也就是本次任务中每个坐标点处的0/1值。
再根据使用的决策边界函数具体分析:
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()])
''' ravel() 作用是将多维数组降为一维行向量 ,np.c_[]作用是按行连接两个矩阵,在此是为了
组成坐标,model是形参,主函数中对plot_decision_boundary()的调用返回了以
np.c_[xx.ravel(), yy.ravel()]为predict()的形参X.'''
# print("z1",Z)
# print(xx)
# print(xx.ravel())
# print(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)
主函数中绘制边界:plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
其中np.meshgrid()函数用来形成网格点,xx为横坐标,yy为纵坐标,且xx,yy的矩阵都是yy的列数 * xx的列数,举个例子:
x = np.arange(-2,2)
y = np.arange(0,3)#生成一位数组,其实也就是向量
x
Out[31]: array([-2, -1, 0, 1])
y
Out[32]: array([0, 1, 2])
z,s = np.meshgrid(x,y)#将两个一维数组变为二维矩阵
z
Out[36]:
array([[-2, -1, 0, 1],
[-2, -1, 0, 1],
[-2, -1, 0, 1]])
s
Out[37]:
array([[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2]])
ravel() 作用是将多维数组降为一维行向量 ,np.c_[ ]作用是按行连接两个矩阵,在此是为了组成坐标,model是形参,主函数中对plot_decision_boundary()的调用返回了以np.c_[xx.ravel(), yy.ravel()]为predict()的形参X。
主函数中绘制边界:plot_decision_boundary(lambda x: predict(parameters, x.T), X, Y)
其中有个lambda需要注意:
lambda表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。
lambda所表示的匿名函数的内容应该是很简单的,如果复杂的话,干脆就重新定义一个函数了,使用lambda就有点过于执拗了。
lambda就是用来定义一个匿名函数的,如果还要给他绑定一个名字的话,就会显得有点画蛇添足,通常是直接使用lambda函数。如下所示:
add = lambda x, y : x+y
add(1,2) # 结果为3
这些细节综合起来就能理解为什么逻辑回归(Logistic Regression)对非线性可分数据集分类效果差了。就是因为通过参考文章的方法,以及结合我所做的任务判断出此数据集不是线性可分的,所以LR方法变现会很差。