吴恩达机器学习第二次作业
这次作业是用logistic回归实现对数据集的分类,logistic回归本质上是用到了sigmoid函数。第一部分是对录取结果的分类。下面先看看数据集。
test1是第一次面试成绩,test2是第二次面试成绩,outcome是录取结果。我们的任务是利用逻辑回归完成对录取结果的预测,也可以看作是进行分类。
刚开始是想到利用梯度下降进行实现,后面才看到有更简便的方法。所以还是先介绍利用梯度下降方法来完成。
首先还是读数据然后处理。
path = 'C:/Users/ASUS/Desktop/2.txt'
df = pd.read_csv(path,header=None,names=['test1', 'test2', 'outcome'])
df.insert(0,'ones',[1]*df.shape[0]) #插入一列全是1的用于和W0相乘
positive = df[df['outcome'].isin([1])].ix[:,1:3] #取出录取结果为1的数据
negative = df[df['outcome'].isin([0])].ix[:,1:3]
x = df.ix[:,:-1]
y = df.ix[:,-1]
x = np.mat(x)
y = np.mat(y)
theta = np.zeros(3)
theta = np.mat(theta)#初始化θ
然后要定义sigmoid函数和梯度更新的函数
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def gradient(x,y,theta,num,lr):
param_num = theta.shape[-1]
loss_list = []
for i in range(num):
theta = theta - lr*((sigmoid(x*theta.T) - (y.T)).T)*x
loss = (y*np.log(sigmoid(x*theta.T)))+((1-y)*np.log(1-(sigmoid(-x*theta.T))))
loss_list.append(loss)
loss_list = [i[0,0] for i in loss_list if np.isnan(i[0,0]) == False] #去除列表中的nan
return theta, loss_list
之后进行训练。可以看出直接使用梯度下降训练次数较大。
theta_1,loss = gradient(x,y,theta,200000,0.01)
最后展示结果。
x_pre = np.linspace(30, 100, 100)
plt.scatter(positive['test1'],positive['test2'])
plt.scatter(negative['test1'],negative['test2'],color = 'r')
plt.plot(x_pre,-theta_1[0,0]/theta_1[0,2]-theta_1[0,1]/theta_1[0,2]*x_pre,color = 'y')
plt.show()
结果如下图。
利用梯度下降大概就是上面这些内容,下面介绍利用scipy中optimize子包自带的优化算法fmin_tnc(),利用fmin_tnc()我们无需定义迭代次数和步长,这个函数可以直接返回所求问题的最优解,也就是θ的最优解。
def cost(theta, x, y):
cost = (y*np.log(sigmoid((theta*x.T)).T)+((1-y)*np.log(1-(sigmoid((theta*x.T)).T))))/(-x.shape[0])
return cost
def gradient(theta, x, y):
return ((sigmoid((theta*x.T).T) - (y.T)).T)*x/(x.shape[0])
import scipy.optimize as opt
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(x, y))
#result返回值是(array([-25.16131853, 0.20623159, 0.20147149]), 36, 0)
result的第一个返回值就是θ,下面展示结果。
x_pre = np.linspace(30, 100, 100)
plt.scatter(positive['test1'],positive['test2'])
plt.scatter(negative['test1'],negative['test2'],color = 'r')
plt.plot(x_pre,-result[0][0]/result[0][2]-result[0][1]/result[0][2]*x_pre,color = 'y')
plt.show()
结果如下图。
另外在利用这个fmin_tnc()时还出现了一些小问题,现在也还没搞清楚。
#x_shape(100,3),y_shape(1,100),theta_shape(1,3)
def cost(theta, x, y):
cost = y*np.log(sigmoid(x*theta.T))+(1-y)*np.log(1-sigmoid(x*theta.T))
return cost/(-x.shape[0])
def gradient(theta, x, y):
return ((sigmoid((theta*x.T).T) - (y.T)).T)*x/(x.shape[0])
import scipy.optimize as opt
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(x, y))
当我定义的cost函数长这样时,使用fmin_tnc()就会报错"shapes (100,3) and (1,3) not aligned: 3 (dim 1) != 1 (dim 0)"但是明明θ已经转置了,后来考虑是不是转置会影响后来θ的形状,但是使用.T转置不应该改变原始θ,这个问题到现在也没搞清楚,严重怀疑和fmin_tnc()函数的内部有关。
第二部分是对芯片测试结果的分类。数据如下。
两类数据的散点图如下。
可以看出两类数据无法用直线分开,需要添加高次项特征用曲线进行分割。
具体代码如下,先介绍不用优化算法fmin_tnc()的原始方法。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
path = 'C:/Users/ASUS/Desktop/3.txt'
df = pd.read_csv(path,header=None,names=['test1', 'test2', 'outcome'])
#添加高次项
num = 6 #最高次为6次
x1 = df['test1']
x2 = df['test2']
for i in range(1,num+1):
for j in range(0,i+1):
df['x1^'+str(i-j)+'x2^'+str(j)] = np.power(x1, i-j) * np.power(x2, j)
df = df.drop(['test1', 'test2'],axis=1)
添加完高次项得到的数据如下,形状为(118,29),除了outcome一列其他作为X。
之后是初始化θ,并划分X和Y。
theta = np.zeros(28)
theta = np.mat(theta)
x = df.iloc[:,1:]
y = df.iloc[:,0]
x = np.mat(x)
y = np.mat(y)
然后定义sigmoid函数,定义梯度下降的函数,与之前不同的是这里我们引入了正则化来防止过拟合,代码如下。
def sigmoid(z):
return 1 / (1 + np.exp(-z))
#lamda是正则化系数,越大容易欠拟合,越小容易过拟合
def gradient(x,y,theta,num,lr,lamda):
param_num = theta.shape[-1]
loss_list = []
for i in range(num):
reg = lamda*theta[0,1:]/x.shape[0]
reg = np.insert(reg,0,values=0,axis=1) #注意不惩罚θ0
theta = theta - (lr*((sigmoid(x*theta.T) - (y.T)).T)*x)-reg
loss_1 = (y*np.log(sigmoid(x*theta.T))+(1-y)*np.log(1-(sigmoid(x*theta.T))))/(-x.shape[0])
loss = loss_1+lamda*(theta*theta.T-theta[0,0]**2)/(2*x.shape[0])
loss_list.append(loss)
loss_list = [i[0,0] for i in loss_list if np.isnan(i[0,0]) == False] #去除列表中的nan
return theta, loss_list
之后进行训练。
theta_1,loss = gradient(x,y,theta,150000,0.001,0.001)
最后展示训练结果,用的是等高线绘图。
m = np.linspace(-1.2,1.2,200)
xx,yy = np.meshgrid(m,m)
xr=xx.ravel()
yr=yy.ravel()
dt = pd.DataFrame()
num = 6 #最高次为6次
for i in range(1,num+1):
for j in range(0,i+1):
dt['xr^'+str(i-j)+'xr^'+str(j)] = np.power(xr, i-j) * np.power(yr, j)
dt.insert(0,'ones',1)
adt = np.array(dt)
z = adt*(theta_1.T)
zz = z.reshape((xx.shape))
plt.scatter(positive['test1'],positive['test2'],label='positive')
plt.scatter(negative['test1'],negative['test2'],color = 'r',label='hegative')
plt.legend()
plt.contour(xx,yy,zz,[0]) #[0]控制等高线的高度值
plt.show()
可以通过改变lamda的值来看效果。
下面我们利用优化算法fmin_tnc()进行求解。对了关于第一部分的疑惑好像有了解答,貌似是因为fmin_tnc()中的第二个参数必须是一维的ndarray数组。代码如下。
theta = np.zeros(28)
def cost(theta, x, y,lamda):
a = np.mat(theta)
b = a.T
loss_1 = (y*(np.log(sigmoid(a*x.T)).T)+(1-y)*(np.log(1-(sigmoid(a*x.T))).T))/(-x.shape[0])
loss = loss_1+lamda*(a*b-theta[0]**2)/(2*x.shape[0])
return loss
def gradient(theta, x, y,lamda):
a = np.mat(theta)
b = a.T
reg = lamda*a[0,1:]
reg = np.insert(reg,0,values=0,axis=1)
gra = (((sigmoid(x*b) - (y.T)).T)*x + reg)/x.shape[0]
return gra
lamda=0
import scipy.optimize as opt
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(x, y,lamda))
theta_2 = np.mat(result[0])
z = adt*(theta_2.T)
zz = z.reshape((xx.shape))
plt.scatter(positive['test1'],positive['test2'],label='positive')
plt.scatter(negative['test1'],negative['test2'],color = 'r',label='hegative')
plt.legend()
plt.contour(xx,yy,zz,[0]) #[0]控制等高线的高度值
plt.show()
可以看出当正则化系数lamda等于0时,为过拟合。
当正则化系数lamda等于0.01时,分割效果还行。
不得不说利用上述优化算法比不用优化算法快多了!