2021-09-26

吴恩达机器学习第二次作业

这次作业是用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时,分割效果还行。
在这里插入图片描述
不得不说利用上述优化算法比不用优化算法快多了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值