前面学了二元分类,但是有些数据是明显可分的,但是无法用一条线将它们分开,而是一个二次曲线,今天就侃侃二次曲线分类$ $

问题

下图如何分类:
raw_picture
显然,用一个圆或者椭圆就可以将它们分开

理论

回想Logistic Regression,它的假设函数是这样的:

\[ \begin{align} &h_\theta(x)=g(\theta^Tx)=\frac{1}{1+e^{-\theta^{T}x}}\\ &g(z)=\frac{1}{1+e^{-z}}\\ &z=\theta_{0}*1+\theta_{1}x_{1}+\cdots +\theta_{n}x_{n} \end{align} \]

其实,\(x_{0}\cdots x_{n}\)可以取的比较特别一点,例如这里:

\[ z=\theta_{0}+\theta_{1}x+\theta_{2}x^2+\theta_{3}y+\theta_{4}y^2 \]

这样我们的特征就是\(x,x^2,y,y^2\),至于为什么是它们其实也很简单,因为二次曲线就是由这几项再加一个偏置项构成的.

代码和结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# encoding=utf8
import numpy as np
import matplotlib.pyplot as plt
# calculate the sigmoid function
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
def Gradient_Descent_Batch(X,y,theta,alpha):
"""batch gradient descent
:type X: numpy.array
:param X: a dataset(m*n)
:type y: numpy.array
:param y: a labels(m*1)
"""
# insert one column with 1
# X=np.insert(X,0,1,axis=1)
m=X.shape[0]
n=X.shape[1]
max_iter=1000
for num in range(0,max_iter):
error=sigmoid(X.dot(theta))-y
old_theta=theta
theta=theta-alpha*X.T.dot(error)
diff_theta=theta-old_theta
if(np.sqrt(diff_theta.T.dot(diff_theta))<0.0001):
break
return theta
def load_data():
"""加载原始数据,以及特征数据构建和组合
  h=theta0*1+theta1*x+theta2*x^2+theta3*y+theta4*y^2
Returns:
points:ndarray类型,(num_samples*2);被分类的点的坐标
y:ndarray类型,(num_samples*1);被分类的点的标签
X:ndarray类型,(num_samples*5);重新构建的特征数据
"""
data=np.loadtxt('./data/quadratic_nonlinear_data.txt')
points=data[:,:-1]
lables=data[:,-1:]
# 在点中插入一列1
X=np.insert(points,0,1,axis=1)
# 在第三,第五列插入相应的特征数据
X=np.insert(X,2,points[:,0]**2,axis=1)
X=np.insert(X,4,points[:,1]**2,axis=1)
return points,X, lables
def plot(points,y,theta):
"""画出二次曲线分类的图
Args:
points:ndarray类型,(num_samples*2);被分类的点的坐标
y:ndarray类型,(num_samples*1);被分类的点的标签
theta:ndarray类型,(5*1);学习到的参数
"""
# 获得sanmple的个数
N=points.shape[0]
# 按类显示
for i in xrange(N):
if int(y[i, 0]) == 0:
plt.plot(points[i, 0], points[i, 1], 'ro')
elif int(y[i, 0]) == 1:
plt.plot(points[i, 0], points[i, 1], 'ko')
# 按隐式函数的结果来显示学习到的二次曲线
x = np.linspace(-2., 4.)
y = np.linspace(-2., 4.)[:, None]
plt.contour(x, y.ravel(), theta[0,0]+theta[1,0]*x+theta[2,0]*x**2+theta[3,0]*y+theta[4,0]*y**2, [0])
# 坐标轴等比例显示
plt.axis('equal')
# plt.axis([-3,5,-2,4])
plt.show()
if __name__ == '__main__':
points,X,y=load_data()
init_theta=np.zeros((5,1))
alpha=0.001
theta=Gradient_Descent_Batch(X,y,init_theta,alpha)
# print theta
plot(points,y,theta)

最后的结果显示:
res_picture