机器学习|吴恩达机器学习之逻辑回归

一个名为回归却用于解决分类问题的算法,吴恩达coursera第三周

逻辑回归(Logic Regression)

代码

数据处理

(1)常规读取数据的方法,值得注意的是X = data.iloc[:, :-1].values.values作用是将DataFrame转化为ndarray。根据手册,更推荐使用.to_numpy()

1
2
3
4
5
6
7
8
9
10
path = 'ex2data1.txt'
data = pd.read_csv(path, names=('exam1', 'exam2', 'admitted'))
data_copy = pd.read_csv(path, names=('exam1', 'exam2', 'admitted'))

# 进一步准备数据,对结构初始化
data.insert(0, 'One', 1)
X = data.iloc[:, :-1].values
y = data.iloc[:, -1].values
theta = np.zeros(
X.shape[1]) # 注意这里theta创建的是一维的数组,对于ndarray一定要注意一维时它的shape(和matrix有很大区别)

数据可视化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'''
数据可视化
'''
def plot_data(data):
cols = data.shape[1]
feature = data.iloc[:, 0:cols - 1]
label = data.iloc[:, cols - 1]
# iloc 根据列的位置索引来切片
postive = feature[label == 1]
negtive = feature[label == 0]

# plt.figure(figsize=(8, 5))
plt.scatter(postive.iloc[:, 0], postive.iloc[:, 1])
plt.scatter(negtive.iloc[:, 0], negtive.iloc[:, 1], c='r', marker='x')
plt.legend(['Admitted', 'Not admitted'], loc=1)
plt.xlabel('Exam1 score')
plt.ylabel('Exam2 score')

假设函数

假设函数:

(1)公式:$h_{\theta}(x)=\frac{1}{1+e^{-\theta^{T}x}}$,又名Sigmoid function,函数图像如下图所示,从图中可以看出$h_{\theta}$的取值范围0~1

(2)$h_{\theta}$的含义是:特征x的情况下,y=1的概率

1
2
def sigmoid(z):
return 1 / (1 + np.exp(-z))

代价函数:

如果这时再使用线性回归的$J(\theta_{0},\theta_{1})=\frac{1}{2m}\sum_{i=1}^{m}(h_{\theta}(x_{i})-y_{i})^{2}$将会导致函数图像不光滑。这样使用最小值法时容易导致无法找到全局最优解。因此需要使用新的代价函数。

AcBilQ.png

(1)公式:Ac0jeI.png

具体解释如下:

AcBVwq.md.png

1
2
3
4
5
6
7
'''
cost function可以用矩阵实现也可以用ndarray实现,更建议使用后者
'''
def cost(theta, X, y):
first = (-y) * np.log(Sigmoid.sigmoid(X @ theta)) # 这里*号是对应位置相乘而不是矩阵运算
second = (1 - y) * np.log(1 - Sigmoid.sigmoid(X @ theta))
return np.mean(first - second)

高级优化法:

除了梯度下降法之外,还有其他几种优化方法,比起gradient descent,这些方法更适合处理大型数据且不需要你设置学习速率。

AcBl6J.md.png

这些方法的具体原理非常复杂,但是,python的模块往往是非常强大的,因此往往只需要你计算一下到导数项即可

1
2
def gradient(theta, X, y):
return (1 / len(X)) * (X.T @ (Sigmoid.sigmoid(X @ theta) - y))

再使用import scipy.optimize as opt中的.fmin_tnc来迭代,得到最终的$\theta$值。

1
2
3
# 这里不使用梯度下降法,换成其他优化算法来迭代
result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(X, y))
final_theta = result[0]

检测准确率

1
2
3
4
5
6
7
8
9
10
11
'''
预测标签
参数:
-参数:theta
-样本:X
返回值:
-预测标签
'''
def predict(theta, X):
probability = Sigmoid.sigmoid(X @ theta)
return [1 if x >= 0.5 else 0 for x in probability]

将得到的预测标签同数据原有的标签进行比对,得到准确率

1
2
3
predictions = predict(final_theta, X)
correct = [1 if a == b else 0 for (a, b) in zip(predictions, y)]
accurcy = np.sum(correct) / len(X) # 准确率89%

也可以自定义一个样本预测一发

1
2
3
# 输入一个数据进行预测
test = np.array([1, 45, 85]).reshape(1, 3)
predict_result = predict(final_theta, test) # 预测值y=1,概率为0.776

决策边界

$h_{\theta}=0.5$时的直线,由假设函数图像可以看出,当$h_{\theta}=0.5$时,$z=\theta^{T}x=0$,这里图中是以$x2$作为纵坐标。

1
2
3
4
5
# 决策边界
def plot_decision_boundary(theta, X):
plot_x = np.linspace(20, 110)
plot_y = -(theta[0] + plot_x * theta[1]) / theta[2]
plt.plot(plot_x, plot_y, c='y')

画图函数

1
2
3
4
plt.figure(figsize=(8, 5))
plot_data(data_copy)
plot_decision_boundary(final_theta, X)
plt.show()

结果图为:

AcBvjJ.md.png

带正则化项的逻辑回归函数

读入数据:

1
2
3
4
5
path = 'ex2data2.txt'
data = pd.read_csv(
path, names=('Microchip Test1', 'Microchip Test2', 'Accept'))
x1 = data.iloc[:, 0].values
x2 = data.iloc[:, 1].values

可视化数据

1
2
3
4
5
6
7
8
9
10
11
12
def plot_data(data):
feature = data.iloc[:, 0:2]
label = data.iloc[:, 2]
positive = feature[label == 1]
negative = feature[label == 0]

plt.scatter(positive.iloc[:, 0].values, positive.iloc[:, 1].values)
plt.scatter(
negative.iloc[:, 0].values,
negative.iloc[:, 1].values,
c='r',
marker='x')

特征映射

由可视化数据可知,如果单独用两个特征,是无法表示出决策边界的(欠拟合underfit)。因此需要映射多个特征。

AcDVjH.png

这里将$x1$和$x2$映射为6个特征值

1
2
3
4
5
6
7
8
9
10
11
def map_feature(x1, x2):
degree = 6

x1 = x1.reshape((x1.size, 1)) # ndarray.size:数组中元素的个数
x2 = x2.reshape((x2.size, 1))
result = np.ones(x1.shape[0]) # 初始化一个值为1的数组(列向量)

for i in range(1, degree + 1):
for j in range(0, i + 1):
result = np.c_[result, (x1**(i - j) * (x2**j))] # np.c_:列拼接
return result # 返回值即为特征值X

但这种映射可能导致过拟合(overfit),泛化能力差,因此还需要正则化(regularization)。

*正则化

(1)原理:

因为过拟合是由于特征项过多引起的,减少特征的数量固然可以,还有一种方法就是正则化:减小$\theta_{j}$的值。

AcDRV1.md.png

由图中可以看出,线性回归算法中添加两个正则化项——也叫惩罚项,1000$\theta_{3}$和1000$\theta_{4}$。上图可以看出,在利用优化算法求解参数时,要想让代价函数值变小,会使得$\theta_{3}$和$\theta_{4}$变得非常小,也就导致了$\theta_{3}x^{3}$和$\theta_{4}x^{4}$非常小,那么图中右边的假设函数就近似与左边的函数了。

实际操作中,我们很多时候并不知道究竟应该惩罚哪一项,所以实际上除了$\theta_{0}$(全是1),所有项都会惩罚。

回到逻辑回归算法上也是一样的

(2)公式:

AcD4PK.md.png

对于线性回归算法也类似:

AcD7KH.png

代价函数:

1
2
3
4
5
6
7
8
9
def cost_reg(theta,X, y, lmd):
# 不惩罚第一项
_theta = theta[1:]
reg = (lmd / (2 * len(X))) * (_theta @ _theta)

first = (y) * np.log(Sigmoid.sigmoid(X @ theta))
second = (1 - y) * np.log(1 - Sigmoid.sigmoid(X @ theta))
final = -np.mean(first + second)
return final + reg

梯度函数

(1)公式:AWJzAf.md.png

(2)向量化:$\theta_{j}:=\theta-\alpha[\frac{1}{m} X^{T} (g(X^{T}\theta)-y)+\frac{\lambda}{m}\theta_{j}]$

代码只需要计算蓝色括号中的内容,然后用优化算法迭代:

1
2
3
4
5
def gradient_reg(theta,X, y, lmd):
# 因为不惩罚第一项,所以要分开计算
grad = (1 / len(X)) * (X.T @ (Sigmoid.sigmoid(X @ theta) - y))
grad[1:] += (lmd / len(X)) * theta[1:]
return grad

优化算法:

用于迭代计算$\theta_{j}$值。

1
2
3
4
5
6
result = opt.fmin_tnc(
func=cost_reg,
x0=theta,
fprime=gradient_reg,
args=(X, y, 1),
)

画出决策边界

不是代入假设函数来画!!在逻辑回归中假设函数时Sigmoid function,用于计算概率的!!

1
2
3
4
5
6
7
8
def plot_decision_boundary(theta):
x=np.linspace(-1,1.5,50)
plot_x,plot_y=np.meshgrid(x,x) # 先画网格

z=map_feature(plot_x,plot_y)
z=z@theta # 画出边界
z=z.reshape(plot_x.shape)
plt.contour(plot_x,plot_y,z,0,colors='yellow')
-------------End-------------
梦想总是要有的,万一有人有钱呢?