赞
踩
本文声明:本文参考深度学习-Stanford吴恩达教授的文章,思路以及代码。基于他的文章(希望大家直接去看原作者的优秀文章【深度学习-Stanford吴恩达教授】,这篇博客仅为了记录自己的学习过程),形成了自己的理解,希望总结成自己的经验与知识,于是发表了这篇博客,如有不妥的地方欢迎大家指正。(若有侵权可联系我进行删除,刚开始写没有经验)
(Logistic Regression)逻辑回归算法适用于二分零算法,这里主要讲逻辑回归的Hypothesis Function(假设函数)。
对于二元分类问题来讲,给定一个输入特征向量 X = {x1,x2,x3,…},它可能对应一张图片,你想识别这张图片识别看它是否是一只猫或者不是一只猫的图片,你想要一个算法能够输出预测,,你只能称之为y,也就是你对实际值y的估计。更正式的说:你想让y表示等于y等于1的一种可能性或者是机会,前提条件是给定了输入特征X。
X是一个nx维的向量(相当于nx个特征的特征向量),我们用w表示逻辑回归的参数,这也是一个nx维向量(因为w实际上是特征权重,维度与特征向量相同),参数里面还有个b,表示偏差。 所以给出输入x以及参数w和b之后,我们怎样产生输出预测值y,答案就是:y = wTx + b。
我们通过yh = wTx + b得到一个关于输入x的线性函数,实际上这是在做线性回归时所用到的,但是这对于二元分类来讲并不是一个非常好的算法,因为你想让yh表示实际值y等于1的机率的话,yh应该在0到1之间。这是一个需要解决的问题,因为wTx + b可能比1要大得多,或者甚至为一个负值。对于你想要的在0和1亿间的概率来说它是没有意义的。
因此在逻辑回归中,我们的输出yh应该是等于由上面得到的线性函数式子作为自变量的sigmoid函数中,公式如上图最下面所示,将线性函数转换为非线性函数。
下图是sigmoid函数的图像,如果我把水平轴作为z轴,那么关于z的sigmoid函数是这样的,它是平滑地从0走向1,让我在这里标记纵轴,这是0,曲线与纵轴相交的截距是0.5,这就是关于的sigmoid函数的图像。 我们通常都使用z来表示wTx十b的值。
关于sigmoid函数的公式是这样的,σ(z)= 1 / (1 + e-z),在这里z是一个实数,这里要说明一些要注意的事情,如果z非常大那么e-z将会接近于0,关于z的sigmoid函数将会近似等于1除以1加上某个非常接近于0的项,因为e的指数如果是个绝对值很大的负数的话,这项将会接近于0,所以如果z很大的话那么关于z的sigmoid函数会非常接近1。相反地,如果z非常小或者说是一个绝对值很大的负数,那么关于e-z这项会变成一-个很大的数,你可以认为这是1除以1加上一个非常非常大的数,所以这个就接近于0。实际上你看到当z变成一个绝对值很大的负数,于z的sigmoid函数就会非常接近于0,
因此当你实现逻辑回归时,你的工作就是去让机器学习参数w以及b,这样才使得y^^^成为对y = 1这一情况的概率的一个很好的估计。
#现在构建sigmoid(),需要使用 sigmoid(w ^ T x + b) 计算来做出预测。 def sigmoid(z) : """ :param z:任何大小的标量或numpy数组。 :return:sigmoid(z) """ s = 1 / (1 + np.exp(-z)) return s print("==========测试sigmoid函数==========") print("sigmoid(0) = " + str(sigmoid(0))) print("sigmoid(10) = " + str(sigmoid(10))) 运行结果: ==========测试sigmoid函数========== sigmoid(0) = 0.5 sigmoid(10) = 0.9999546021312976
损失函数(Cost Function)为什么需要代价函数:为了训练逻辑回归模型的参数w和参数b,我们需要一个代价函数,通过训练代价函数来得到参数w和参数b。先看一下逻辑回归的输出函数:
为了让模型通过学习调整参数,你需要给予一个m样本的训练集,这会让你在训练集上找到参数w和参数b,来得到你的输出。
对训练集的预测值,我们将它写成y^^^,我们更希望它会接近于训练集中的y值(已大打好标签,在二分类里里面为0 / 1),为了对上面的公式更详细的介绍,我们需要说明上面的定义是对一个训练样本来说的,这种形式也使用于每个训练样本,我们使用这些带有圆括号的上标来区分索引和样本,训练样本i所对应的预测值是y(i),是用训练样本的wTx(i) + b然后通过sigmoid函数来得到,也可以把z定义为z(i) = wTx(i) + b,我们将使用这个符号(i)注解,上标(i)来指明数据表示x或者y或者z或者其他数据的第i个训练样本,这就是上标(i)的含义。
**损失函数(Loss function):**损失函数又叫做误差函数,用来衡量算法的运行情况,Loss function: L(y^^^ ,y)。我们通过这个称为L的损失函数,来衡量预测输出值和实际值有多接近。一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中我们不这么做,因为当我们在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值,虽然平差是一个不错的损失函数,但是我们在逻辑回归模型中会定义另外一个损失函数。我们在逻辑回归中用到的损失函数是:
L(y^^^,y) = -ylog(y^^^) - (1 - y)log(1-y^^^)
#初始化参数的函数已经构建好了,现在就可以执行“前向”和“后向”传播步骤来学习参数。 #现在要实现一个计算成本函数及其渐变的函数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] #m = 209 训练集的图片数量 #正向传播 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 == ()) #创建一个字典, 把dw, db保存起来 grads = { "dw" : dw, "db" : db } return (grads, cost) #写好之后我们来测试一下 print("========= 测试propagate =========") #初始化参数 w = np.array( [[1],[2]] ) b = 2 X = np.array( [ [1,2], [3,4]]) Y = np.array([ [1, 0]]) # print("w = " + str(w)) # print("X = " + str(X)) # print("Y = " + str(Y)) 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.49993523062470574 cost = 6.000064773192205
度下降法可以做什么:在测试集上,通过最小化代价函数(成本函数)**J(w,b)**来训练参数w和b,前面已学的如下:(即找到)**J(w,b)**值最小时,参数w和b的值)
emsp;这里讨论怎样通过计算偏导数来实现逻辑回归的梯度下降算法。它的关键点几个重要公式,其作用是用来实现逻辑回归中梯度下降算法。但是在本节视频中,我将使用计算图对梯度下降算法进行计算。接下来让我们开始学习逻辑回归的梯度下降算法。
为了使得逻辑回归中最小代价函数J(a,y),我们仅仅需要修改参数w和b,so仅需要对w和b求偏导:
''' 现在,我要使用渐变下降更新参数。 目标是通过最小化成本函数J来学习 w和b。 对于参数 θ 更新规则是 $ \theta = \theta - \alpha \text{ } d\theta$,其中 α是学习率。 ''' 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) #现在就让我们来测试一下优化函数: 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.5593049248448891 dw = [[0.90158428] [1.76250842]] db = 0.4304620716786828
emsp;###### 6.1向量化的显著优势:
emsp;###### 6.2向量化 Logistic 回归
emsp;###### 6.3向量化 Logistic 回归的梯度输出
''' 就目前而言,基本上把所有的东西都做完了, 现在我们要把这些函数统统整合到一个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_prediction_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"])
print("costs = " + str(costs))
plt.plot(costs)
plt.ylabel('costs')
plt.xlabel('iterations (per hundreds)')
plt.title("Learning rate =" + str(d["learning_rate"]))
plt.show()
''' 让我们进一步分析一下,并研究学习率alpha的可能选择。为了让渐变下降起作用, 我们必须明智地选择学习速率。学习率α \alphaα 决定了我们更新参数的速度。 如果学习率过高,我们可能会“超过”最优值。同样, 如果它太小,我们将需要太多迭代才能收敛到最佳值。这就是为什么使用良好调整的学习率至关重要的原因。 我们可以比较一下我们模型的学习曲线和几种学习速率的选择。 也可以尝试使用不同于我们初始化的learning_rates变量包含的三个值,并看一下会发生什么。 ''' learing_rates =[0.01, 0.001, 0.0001] models = {} for i in learing_rates: print("learing 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 learing_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_faceclor('0.90') plt.show()
运行结果:
learing rate is : 0.01
训练集准确性: 99.52153110047847 %
测试集准确性: 68.0 %
learing rate is : 0.001
训练集准确性: 88.99521531100478 %
测试集准确性: 64.0 %
learing rate is : 0.0001
训练集准确性: 68.42105263157895 %
测试集准确性: 36.0 %
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。