开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
文章有问题......
在前面的实验,我们发现
features_temp = pd.read_csv('iris.csv').iloc[:, 1: 3].values
labels_temp = pd.read_csv('iris.csv').iloc[:, -1].values
labels_temp[labels_temp != 'Iris-setosa'] = 0
labels_temp[labels_temp == 'Iris-setosa'] = 1
labels = labels_temp.astype(float).reshape(-1, 1)
features = np.concatenate([features_temp, np.ones(shape=labels.shape)], 1)
np.random.seed(24)
batch_size = 10
num_epoch = 200
lr_init = 0.5
n = features.shape[1]
w = np.random.randn(n, 1)
lr_lambda = lambda epoch: 0.95 ** epoch
for i in range(num_epoch):
w = sgd_cal(features, w, labels, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
w
---
array([[ 2.8630463 ],
[-3.19680442],
[ 0.01148845]])
logit_acc(features, w, labels, thr=0.5)
---
1.0
这里我们是用np.random.randn(n, 1)
,来随机选择初始值w的,后面我们又手动指定了w的初始值
w = np.array([[10.0],[-1],[0]])
for i in range(num_epoch):
w = sgd_cal(features, w, labels, logit_gd, batch_size=batch_size, epoch=1, lr=lr_init*lr_lambda(i))
w
---
array([[ 6.79050273],
[-6.44217682],
[-1.0895957 ]])
logit_acc(features, w, labels, thr=0.5)
---
1.0
我们又得到了一组w的值,并且如果我们用一个列表在循环中间记录w的变化可以发现,两次梯度下降都已经收敛,但是我们仍然得到了两个不同的值,这是因为我们此时全域最小值不是一个单独的解,而是一个解集。
我们先看一下散点图
plt.scatter(features[(labels == 0).flatten(), 0], features[(labels == 0).flatten(), 1], color='red')
plt.scatter(features[(labels == 1).flatten(), 0], features[(labels == 1).flatten(), 1], color='blue')
显然在中间,我们可以画出很多条直线使得逻辑回归的损失函数为0,因此其解为一个集合
进一步的,我们可以绘制一下当d=0时解集
x1 = np.linspace(-1,10,110)
x2 = np.linspace(-10,1,110)
x11,x22 = np.meshgrid(x1,x2)
xx = np.concatenate([x11.reshape(-1, 1),x22.reshape(-1, 1),np.zeros((x1.shape[0] * x2.shape[0],1))],1)
xx_df = pd.DataFrame(xx)
xx_df[3] = xx_df.apply(lambda x:logit_acc(features,[[x[0]],[x[1]],[x[2]]],labels),axis=1)
xx = xx_df.values
plt.contourf(xx[:,0].reshape(x11.shape),xx[:,1].reshape(x11.shape),xx[:,3].reshape(x11.shape))
其实其中黄色的就是解集,我们单独把它标记出来
其实黄色的不是解集,只是表示此处准确率是1.0,但是实际上逻辑回归的损失函数并不是准确率,而是交叉熵函数,因此黄色区域中的部分点如果执行逻辑回归的梯度下降,数值仍然会变化(所以说这篇文章写的就不对)
plt.contourf(xx[:,0].reshape(x11.shape),xx[:,1].reshape(x11.shape),(xx[:,3] == 1).astype(float).reshape(x11.shape),cmap='binary')
不会画三维图,所以只花了d=0的情况,实际上应该可以在三维空间中表示,大致图像应该是一个三棱柱,继续画d=0,2,4,6,8,10的截面图可以观察一下
x1 = np.linspace(-3,3,60) x2 = np.linspace(-5,1,60) x11,x22 = np.meshgrid(x1,x2) fig,axes = plt.subplots(3,2) axes = axes.flatten() for i in range(0,6): xx = np.concatenate([x11.reshape(-1, 1),x22.reshape(-1, 1),np.full_like(x11.reshape(-1, 1),i*2)],1) xx_df = pd.DataFrame(xx) xx_df[3] = xx_df.apply(lambda x:logit_acc(features,[[x[0]],[x[1]],[x[2]]],labels),axis=1) xx = xx_df.values axes[i].contourf(xx[:,0].reshape(x11.shape),xx[:,1].reshape(x11.shape),(xx[:,3] == 1).astype(float).reshape(x11.shape),cmap='binary')
pandas里面DataFrame有个apply方法,如果使用lambda函数,例如
np.random.seed(20) df = pd.DataFrame(np.random.randint(0,10,(3,2))) df --- 0 1 0 3 9 1 4 6 2 7 2 df[2] = df.apply(lambda x:x[0] * x[1] -1,1) # 这里第一个参数为lambda函数,第二个指示x代表什么,当x=1的时候x就是每行数据,因此一般用此方法根据每一行已知数据来添加新列 df --- 0 1 2 0 3 9 26 1 4 6 23 2 7 2 13 def f1(x,y): return x+y df[3] = df.apply(lambda x:f1(x[0],x[1]),1) # lambda函数中的x可以当做参数传入自定义的函数中 df --- 0 1 2 3 0 3 9 26 12 1 4 6 23 10 2 7 2 13 9 np.random.seed(20) df = pd.DataFrame(np.random.randint(0,10,(3,2))) df --- 0 1 0 3 9 1 4 6 2 7 2 df.loc[3,:] = df.apply(lambda x:x[2] - x[1],0) # 这里因为第二个参数指定为0,因此x就是df的每一列,其实[2]、[1],就是第二行、第一行,df.loc[3,:]表明增加第三行 df --- 0 1 0 3.0 9.0 1 4.0 6.0 2 7.0 2.0 3 3.0 -4.0