文章目录
【更新】:找到一篇文章解释什么是正则化、为什么使用正则化,请戳 - > 链接
【2】正则化逻辑回归
在训练的第二部分,我们将要通过加入正则项提升逻辑回归算法。简而言之,正则化是成本函数中的一个术语,它使算法更倾向于“更简单”的模型(在这种情况下,模型将更小的系数)。这个理论助于减少过拟合,提高模型的泛化能力。这样,我们开始吧。
题目:设想你是工厂的生产主管,你有一些芯片在两次测试中的测试结果。对于这两次测试,你想决定是否芯片要被接受或抛弃。为了帮助你做出艰难的决定,你拥有过去芯片的测试数据集,从其中你可以构建一个逻辑回归模型。
1.Visualizing the data
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
path = 'ex2data2.txt'
df = pd.read_csv(path, header=None,names=['Test1', 'Test2', 'Accepted'])
df.head()
def plot_data():
positive = df[df['Accepted'].isin([1])]
negative = df[df['Accepted'].isin([0])]
fig,ax = plt.subplots(figsize=(12,8),dpi=80)
ax.scatter(positive['Test1'],positive['Test2'],marker='o',c='b',label='Accepted')
ax.scatter(negative['Test1'],negative['Test2'],marker='x',c='r',label='Accepted')
ax.legend()
x_label='Test1'
x_label='Test2'
plot_data()
注意到其中的正负两类数据并没有线性的决策界限。因此直接用logistic回归在这个数据集上并不能表现良好,因为它只能用来寻找一个线性的决策边界。
提供一个新的方法:用像逻辑回归这样的线性技术来构造从原始特征的多项式中得到的特征。通过原特征来增加特征量总量(线性组合多次方)
2.Feature mapping
我把已有的这些特征映射到所有的x1和x2的多项式项上,(升高幂数使线性直线变成曲线)直到第六次幂,即x1和x2的次方和从1升为6,h(x)从3项升为28项,同时θ变为28个。
def feature_mapping(x,y,power,as_ndarray=False):
data = {"f'{}{}".format( i-p , p ):np.power(x,i-p) * np.power(y,p)
for i in np.arange(power+1)
for p in np.arange(i+1)
}
if as_ndarray:
return np.array(pd.DataFrame(data))
else:
return pd.DataFrame(data)
x1 = np.array(df.Test1)
x2 = np.array(df.Test2)
data = feature_mapping(x1,x2,power=6)
print(data.shape)
data.head()
经过映射,我们将有两个特征的向量转化成了一个28维的向量。
在这个高维特征向量上训练的logistic回归分类器将会有一个更复杂的决策边界,当我们在二维图中绘制时,会出现非线性。
虽然特征映射允许我们构建一个更有表现力的分类器,但它也更容易过拟合。在接下来的练习中,我们将实现正则化的logistic回归来拟合数据,并且可以看到正则化如何帮助解决过拟合的问题。
3.Regularized Cost function
λ的大小控制新产生的特征变量对代价函数的影响,注意不惩罚第一项即θ0,因为θ0控制的是截距,截距不是越小越好,而应该符合实际要求。
theta = np.zeros(data.shape[1])
X = feature_mapping(x1,x2,power=6,as_ndarray=True)
print(X.shape) #(118, 28)
y = np.array(df.iloc[:,-1])
print(y.shape) #(118,)
def sigmoid(z):
return 1 /(1 + np.exp(-z))
'''
与正则化做对比
def cost(theta,X,y):
return np.mean(-y *np.log(sigmoid(X@theta)) - (1-y)*np.log(1-sigmoid(X@theta)))
'''
def regularized_gradient(theta, X, y, l=1):
thetaReg = theta[1:]
first = ( -y * np.log(sigmoid(X @ theta) )) - (1-y) * np.log(1-sigmoid( X @ theta ))
reg = (thetaReg @ thetaReg) * l / ( 2*len(X) )
return np.mean(first) + reg
regularized_cost(theta,X,y,l=1)
#0.6931471805599454
4.Regularized gradient
我们要使用梯度下降法令这个代价函数最小化,与正常的梯度下降相比多了后面lamda那项,所以重新定义梯度下降函数。
注意:不需要对 𝜃0 进行正则化,因为𝜃0是偏置项通常不考虑,j从1-n,不惩罚第一项
'''
与正则化做对比
def gradient(theta,X,y):
return ( X.T @ (sigmoid(X @ theta) - y)) / len(X)
'''
def regularized_gradient(theta, X, y, l=1):
thetaReg = theta[1:]
first = ( X.T @ (sigmoid(X @ theta) - y)) / len(X)
# 这里人为插入一维0,使得对theta_0不惩罚,方便计算
reg = np.concatenate([np.array([0]), (l / len(X)) * thetaReg])
return first + reg
regularized_gradient(theta,X,y)
'''
array([8.47457627e-03, 1.87880932e-02, 7.77711864e-05, 5.03446395e-02,
1.15013308e-02, 3.76648474e-02, 1.83559872e-02, 7.32393391e-03,
8.19244468e-03, 2.34764889e-02, 3.93486234e-02, 2.23923907e-03,
1.28600503e-02, 3.09593720e-03, 3.93028171e-02, 1.99707467e-02,
4.32983232e-03, 3.38643902e-03, 5.83822078e-03, 4.47629067e-03,
3.10079849e-02, 3.10312442e-02, 1.09740238e-03, 6.31570797e-03,
4.08503006e-04, 7.26504316e-03, 1.37646175e-03, 3.87936363e-02])
'''
5.Learning parameters
import scipy.optimize as opt
print('init cost = {}'.format(regularized_cost(theta,X,y)))
#init cost = 0.6931471805599454
res = opt.minimize(fun=regularized_cost,x0=theta,args=(X,y),method='Newton-CG',jac=regularized_gradient)
res
fun: 0.5290027297127382
jac: array([ 1.95030645e-07, -6.09886279e-08, -3.57089544e-08, -6.02277719e-08,
-2.81813796e-09, 7.83981654e-08, 4.98732575e-09, -4.15055549e-08,
-2.56875984e-08, 4.49783508e-08, -7.10581801e-08, -2.08123947e-08,
-6.66285147e-08, -1.11347159e-08, 9.69909734e-09, -8.23666486e-09,
-1.64013453e-08, -2.91513515e-08, -1.96143288e-08, -9.04704059e-09,
2.55461632e-08, -6.68849932e-08, -1.00026487e-08, -3.61629532e-08,
-1.66562413e-08, -2.46993608e-08, -6.80697551e-09, 1.88441130e-08])
message: 'Optimization terminated successfully.'
nfev: 7
nhev: 0
nit: 6
njev: 68
status: 0
success: True
x: array([ 1.27273965, 0.62527115, 1.18108896, -2.01995957, -0.91742333,
-1.43166409, 0.12400736, -0.36553439, -0.35724011, -0.17512959,
-1.45815636, -0.05098975, -0.61555687, -0.27470641, -1.19281806,
-0.24218774, -0.20600582, -0.04473168, -0.2777848 , -0.29537809,
-0.45635746, -1.04320307, 0.02777141, -0.29243218, 0.01556621,
-0.3273803 , -0.14388698, -0.92465303])
6.Evaluating logistic regression
可以使用预测函数来查看我们的方案在训练数据上的准确度
def predict(theta, X):
probability = sigmoid( X @ theta)
return [1 if x >= 0.5 else 0 for x in probability] # return a list
from sklearn.metrics import classification_report
final_theta = res.x
y_predict = predict(final_theta, X)
predict(final_theta, X)
print(classification_report(y,y_predict))
'''
precision recall f1-score support
0 0.87 0.75 0.80 60
1 0.77 0.88 0.82 58
avg / total 0.82 0.81 0.81 118
'''
7.Decision boundary
方法1:
方法2:
此题中要找出n个点(x1,x2)代入后满足theta.T* X=0,那么可以设z=theta.T*X,即画出z=0时的图像问题,此时(xx,yy,z)为三维空间上的一点。而这个问题恰好可以用plt.contour()函数来实现。
x = np.linspace(-1, 1.5, 50)
#从-1到1.5等间距取出50个数
xx, yy = np.meshgrid(x, x)
#将x里的数组合成50*50=250个坐标
z = np.array(feature_mapping(xx.ravel(), yy.ravel(), 6))
z = z @ final_theta
z = z.reshape(xx.shape)
plot_data()
plt.contour(xx, yy, z, 0, colors='black')
#等高线是三维图像在二维空间的投影,0表示z的高度为0
plt.ylim(-.8, 1.2)
所的图像如下:
np.meshgrid用法:
a = np.array([1,2,3]) #a.shape (3,)
b = np.array([11,22,33,44]) #b.shape (4,)
x,y = np.meshgrid(a,b)
# 返回list,有两个元素,第一个元素是X轴的取值,第二个元素是Y轴的取值
x #x.shape(4,3)
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[1, 2, 3] ])
y #y.shape(4,3)
array([[11, 11, 11],
[22, 22, 22],
[33, 33, 33],
[44, 44, 44]])
plt.contour用法:
plt.contour用来画等高线,等高线是三维图像在二维空间的投影。
a = np.array([1,2,3])
z = np.array([[0,1,1],[1,0,1],[1,1,0]])
x,y = np.meshgrid(a,a)
plt.contour(x,y,z,3) #数字3是等高线的数量
contourf() 用法:
contourf() 返回填充等高线图,其他输入输出和contour()没有区别
a = np.array([1,2,3])
z = np.array([[0,1,1],[1,0,1],[1,1,0]])
x,y = np.meshgrid(a,a)
plt.contourf(x,y,z,3)