目录
逻辑回归模型理论
之前文章中提到监督学习的应用可分为两类:线性回归和逻辑回归。和线性回归不同,逻辑回归输出只有0和1。对于一个逻辑回归任务,可以先使用线性回归来预测y。然而我们希望逻辑回归预测模型输出的是0和1,为了达到这个目的,我们使用sigmoid()来把线性回归预测的输出y映射到0和1之间。
sigmoid公式为:
公式中的z表示的是线性回归模型中输出的结果y。m个training data的情况下,y是一个含m个value的向量。
sigmoid公式代码实现很简单:
def sigmoid(z):
"""
Compute the sigmoid of z
Args:
z (ndarray): A scalar, numpy array of any size.
Returns:
g (ndarray): sigmoid(z), with the same shape as z
"""
g = 1/(1+np.exp(-z))
return g
用图形表示sigmod的输出样子是:可以看到输出区间始终在0到1之间,输入z越大,越趋近于1,反之趋近于0
![](https://img-blog.csdnimg.cn/b138796d064b4c41a9f9f811c10d3cc8.png)
逻辑回归的公式与线性回归类似:
一句话总结逻辑回归模型:线性回归模型+sigmoid = 逻辑回归模型
决策边界
逻辑回归输出的是0和1,由于sigmoid函数是在无穷大的时候趋近于1,无穷小的时候趋近于0,。那么0和1的分界点在哪里,如何判断应当输出0还是应当输出1,这就需要设计一个阈值,来区分开这两种输出。
现在假设阈值为0.5:
- g(z) >=0.5, y=1
- g(z)<0.5, y=0
那边边界点就在g(z)=0.5。由图一可以看出z=0时,g(z) = 0.5。因此边界点z = 0。而z 是线性回归的输出w*x+b。所以得出w*x+b = 0为分界线,即决策边界。
下面用一个例子来实现这条决策边界:
假设有一组training data,每个训练数据包含2个特征值
X = np.array([[0.5, 1.5], [1,1], [1.5, 0.5], [3, 0.5], [2, 2], [1, 2.5]])
y = np.array([0, 0, 0, 1, 1, 1]).reshape(-1,1)
fig,ax = plt.subplots(1,1,figsize=(4,4))
plot_data(X, y, ax)
ax.axis([0, 4, 0, 3.5])
ax.set_ylabel('$x_1$')
ax.set_xlabel('$x_0$')
plt.show()
分布入下图:
逻辑回归模型为:
假设训练完成之后的w_0=1,w_1=1,b=-3。那么这个模型最终公式是:
这里同样假设:
- g(z) >=0.5, y=1
- g(z)<0.5, y=0
得出来决策边界函数为:
决策边界图如图二,左下角蓝色部分为y=0,右上角为y=1:
# Choose values between 0 and 6
x0 = np.arange(0,6)
x1 = 3 - x0
fig,ax = plt.subplots(1,1,figsize=(5,4))
# Plot the decision boundary
ax.plot(x0,x1, c="b")
ax.axis([0, 4, 0, 3.5])
# Fill the region below the line
ax.fill_between(x0,x1, alpha=0.2)
# Plot the original data
plot_data(X,y,ax)
ax.set_ylabel(r'$x_1$')
ax.set_xlabel(r'$x_0$')
plt.show()
![](https://img-blog.csdnimg.cn/1edb46374b47479cb92ad3a5485ef72c.png)
Cost Function : 代价函数
之前已经实现过线性回归模型的代价函数,那么线性回归的代价模型能不能用到逻辑回归中呢?答案是否定的,因为逻辑回归模型中存在非线性因素,如果直接运用线性模型的代价函数就会出现很多局部最小值,影响最佳w,b组合的寻找。图三中左边是线性回归模型的代价函数,可以看到是凸面的,存在一个最小值,而右图是在逻辑回归模型中应用线性回归的代价函数得到的代价函数结果,可以看到有很多局部最小值,但并非全局最小值。因此线性回归中的代价函数并不能应用到逻辑回归模型中。
![图三](https://img-blog.csdnimg.cn/1fb2fcc45346468aa133f812bed3602a.png)
因此逻辑回归模型的代价函数使用下面的公式实现:
代码实现为:
def compute_cost_logistic(X, y, w, b):
"""
Computes cost
Args:
X (ndarray (m,n)): Data, m examples with n features
y (ndarray (m,)) : target values
w (ndarray (n,)) : model parameters
b (scalar) : model parameter
Returns:
cost (scalar): cost
"""
m = X.shape[0]
cost = 0.0
for i in range(m):
z_i = np.dot(X[i],w) + b
f_wb_i = sigmoid(z_i)
cost += -y[i]*np.log(f_wb_i) - (1-y[i])*np.log(1-f_wb_i)
cost = cost / m
return cost
验证一下上面的cost function是否正确:
w_tmp = np.array([1,1])
b_tmp = -3
print(compute_cost_logistic(X_train, y_train, w_tmp, b_tmp))
输出为:0.36686678640551745
梯度下降
梯度下降公式:
对于逻辑回归模型,𝑓𝐰,𝑏(𝑥(𝑖))是预测模型函数,𝑧=𝐰⋅𝐱+𝑏,𝑓𝐰,𝑏(𝑥)=𝑔(𝑧),
计算逻辑回归梯度值:
上面公式2和公式3是梯度值的计算公式,首先初始化dj_dw和dj_db,
计算dj_dw:对于每个训练数据,计算和真实值的误差g(x(i))-y_i,由于训练数据有j个特征值,所以一个训练数据中的j个特征值的误差都要乘以x_j并求和。
def compute_gradient_logistic(X, y, w, b):
"""
Computes the gradient for linear regression
Args:
X (ndarray (m,n): Data, m examples with n features
y (ndarray (m,)): target values
w (ndarray (n,)): model parameters
b (scalar) : model parameter
Returns
dj_dw (ndarray (n,)): The gradient of the cost w.r.t. the parameters w.
dj_db (scalar) : The gradient of the cost w.r.t. the parameter b.
"""
m,n = X.shape
dj_dw = np.zeros((n,)) #(n,)
dj_db = 0.
for i in range(m):
f_wb_i = sigmoid(np.dot(X[i],w) + b) #(n,)(n,)=scalar
err_i = f_wb_i - y[i] #scalar
for j in range(n):
dj_dw[j] = dj_dw[j] + err_i * X[i,j] #scalar
dj_db = dj_db + err_i
dj_dw = dj_dw/m #(n,)
dj_db = dj_db/m #scalar
return dj_db, dj_dw
计算梯度下降值:
def gradient_descent(X, y, w_in, b_in, alpha, num_iters):
"""
Performs batch gradient descent
Args:
X (ndarray (m,n) : Data, m examples with n features
y (ndarray (m,)) : target values
w_in (ndarray (n,)): Initial values of model parameters
b_in (scalar) : Initial values of model parameter
alpha (float) : Learning rate
num_iters (scalar) : number of iterations to run gradient descent
Returns:
w (ndarray (n,)) : Updated values of parameters
b (scalar) : Updated value of parameter
"""
# An array to store cost J and w's at each iteration primarily for graphing later
J_history = []
w = copy.deepcopy(w_in) #avoid modifying global w within function
b = b_in
for i in range(num_iters):
# Calculate the gradient and update the parameters
dj_db, dj_dw = compute_gradient_logistic(X, y, w, b)
# Update Parameters using w, b, alpha and gradient
w = w - alpha * dj_dw
b = b - alpha * dj_db
# Save cost J at each iteration
if i<100000: # prevent resource exhaustion
J_history.append( compute_cost_logistic(X, y, w, b) )
# Print cost every at intervals 10 times or as many iterations if < 10
if i% math.ceil(num_iters / 10) == 0:
print(f"Iteration {i:4d}: Cost {J_history[-1]} ")
return w, b, J_history #return final w,b and J history for graphing
梯度下降测试:
w_tmp = np.zeros_like(X_train[0])
b_tmp = 0.
alph = 0.1
iters = 10000
w_out, b_out, _ = gradient_descent(X_train, y_train, w_tmp, b_tmp, alph, iters)
print(f"\nupdated parameters: w:{w_out}, b:{b_out}")
输出:可以看出结果是不断收敛的
Iteration 0: Cost 0.684610468560574
Iteration 1000: Cost 0.1590977666870457
Iteration 2000: Cost 0.08460064176930078
Iteration 3000: Cost 0.05705327279402531
Iteration 4000: Cost 0.04290759421682
Iteration 5000: Cost 0.03433847729884557
Iteration 6000: Cost 0.02860379802212006
Iteration 7000: Cost 0.02450156960879306
Iteration 8000: Cost 0.02142370332569295
Iteration 9000: Cost 0.019030137124109114
updated parameters: w:[5.28 5.08], b:-14.222409982019837