完成练习之前先看一下Pandas中isin()函数的用法:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randn(4, 4), columns=['A', 'B', 'C', 'D'])
print(df)
print(df.A > 0)
print(df[df.A > 0])
结果:
A B C D
0 -0.402700 -0.948588 0.365819 -0.464737
1 -1.053793 1.975878 0.073766 -2.059584
2 -1.118388 0.539565 1.523708 0.043024
3 1.210605 1.281088 0.178852 -0.708966
0 False
1 False
2 False
3 True
A B C D
3 1.210605 1.281088 0.178852 -0.708966
定义一个DataFrame类型的数据df,4行4列随机产生,行坐标自动生成,列坐标定义为A,B,C,D,可以看到第一个输出结果。df.A代表df的A列,判断是否大于0,大于0的输出True,否则输出False,由此得到第二个输出结果。df[df.A > 0],可以看做df.A>0由布尔值变成了其对应的真实数值。例如,上面的例子中df.A>0的行数是3,其值就是3,所以df[df.A > 0]输出的就是行数为3的数据。
介绍isin()函数,就是为了在画散点图的时候方便把录取和未录取的同学区分开来。
编程练习2:逻辑回归
1. Logistic回归
在这部分练习中,您将建立一个逻辑回归模型来预测学生是否被录取进入大学。假设您是大学系的管理员,并且您希望根据他们在两门考试中的成绩来确定每位申请人的入学机会。您拥有以前申请人的历史数据,您可以将其用作逻辑回归的训练集。对于每个培训示例,您都有申请人在两门考试中的分数和录取决定。您的任务是建立一个分类模型,根据这两个考试的分数估算申请人的录取概率。
1.1 可视化数据
数据集的每一行代表一个学生的信息,有3列。前两列代表两门考试的成绩,最后一列代表是否被录取进大学,1表示被录取,0表示没有被录取。以散点图的形式将数据可视化,录取和没有录取用两种颜色标记。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 画出散点图
path = r'C:\Users\Administrator\Desktop\ML\machine-learning-ex2\ex2\ex2data1.txt'
data = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted']) # 将数据读出,并为每列设置名字
positive = data[data['Admitted'].isin([1])] # 用isin()函数区分正负数据,分别用positive和negative存储
negative = data[data['Admitted'].isin([0])]
fig, ax = plt.subplots(figsize=(12, 8)) # 设置图像
ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted') # 用蓝色和o表示正数据集
ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted') # 用红色和x表示负数据集
ax.set_xlabel('Exam 1 Score') # 设置x,y坐标
ax.set_ylabel('Exam 2 Score')
plt.show()
运行结果:
1.2 履行
这一部分,我们会定义Sigmod函数和代价函数,对参数进行优化,最后评估拟合效果。
Sigmod函数:
g
(
z
)
=
1
1
+
e
−
z
g\left( z \right)=\frac{1}{1+{{e}^{-z}}}
g(z)=1+e−z1
代价函数:
J
(
θ
)
=
1
m
∑
i
=
1
m
[
−
y
(
i
)
log
(
h
θ
(
x
(
i
)
)
)
−
(
1
−
y
(
i
)
)
log
(
1
−
h
θ
(
x
(
i
)
)
)
]
J\left( \theta \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{[-{{y}^{(i)}}\log \left( {{h}_{\theta }}\left( {{x}^{(i)}} \right) \right)-\left( 1-{{y}^{(i)}} \right)\log \left( 1-{{h}_{\theta }}\left( {{x}^{(i)}} \right) \right)]}
J(θ)=m1i=1∑m[−y(i)log(hθ(x(i)))−(1−y(i))log(1−hθ(x(i)))]
- 定义Sigmod和代价函数
data.insert(0, 'Ones', 1) # 在原有数据基础上插入一列,令值全为1,名称为“Ones”,作为Xo
X = data.iloc[:, :3] # 切取前3列作为输入X
y = data.iloc[:, -1] # 切取最后一列作为输出y
theta = np.zeros(3) # 初始化模型参数
# 定义Sigmoid函数
def Sigmoid(z):
return 1 / (1 + np.exp(-z))
# 定义代价函数
def CostFunction(X, y, theta):
theta = np.mat(theta) # 将X,y,theta都变成矩阵形式
X = np.mat(X)
y = np.mat(y)
first = np.multiply(-y.T, np.log(Sigmoid(X * theta.T))) # 定义损失函数的两个部分
second = np.multiply((1 - y.T), np.log(1 - Sigmoid(X * theta.T)))
return np.sum(first - second) / (len(X))
print(CostFunction(X, y, theta))
运行结果:
0.6931471805599453
- 定义梯度下降,利用高级优化算法优化参数
# 定义梯度下降,只迭代一次,求出各偏导即可
def GradientDescent(theta, X, y):
theta = np.mat(theta) # 将X,y,theta都变成矩阵形式
X = np.mat(X)
y = np.mat(y)
y = y.T
grad = np.zeros(3) # grad用来存储各参数的偏导数
error = Sigmoid(X * theta.T) - y
for i in range(3):
term = np.multiply(error, X[:, i])
grad[i] = np.sum(term) / len(X)
return grad
# 利用高级优化算法,result结果的第一个参数就是训练好的模型参数
result = opt.fmin_tnc(func=CostFunction, x0=theta, fprime=GradientDescent, args=(X, y))
print(result)
print("优化好的参数为: ", result[0])
运行结果:
(array([-25.16131872, 0.20623159, 0.20147149]), 36, 0)
优化好的参数为: [-25.16131872 0.20623159 0.20147149]
# CostFunction和GradientDescent中参数的位置不要交换,会报错
- 利用优化好的参数判定边界
# 根据梯度下降得到的参数画出拟合模型
x = np.linspace(data.Exam1.min(), data.Exam1.max(), 100)
f = (-result[0][0] - result[0][1]*x)/result[0][2] # 判定边界就是h=0的曲线,令h=0画出曲线
fig, ax = plt.subplots(figsize=(12, 8))
ax.plot(x, f, 'r', label='Prediction')
ax.scatter(positive['Exam1'], positive['Exam2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam1'], negative['Exam2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
ax.set_title('Predicted IsAdmitted')
plt.show()
运行结果:
- 最后实现预测功能,输入两门课的成绩判断学生被录取的概率
def predict(theta, X):
theta = np.mat(theta)
probability = Sigmoid(theta*X)
return probability
X = np.mat([[1], [45], [85]])
print("第一门课45分,第二门课85分的学生被录取的概率为:", predict(result[0], X))
运行结果:
第一门课45分,第二门课85分的学生被录取的概率为: [[0.77629063]]
完整代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as opt
# 画出散点图
path = r'C:\Users\Administrator\Desktop\ML\machine-learning-ex2\ex2\ex2data1.txt'
data = pd.read_csv(path, header=None, names=['Exam1', 'Exam2', 'Admitted'])
positive = data[data['Admitted'].isin([1])] # 利用isin()函数区分录取和未录取的数据
negative = data[data['Admitted'].isin([0])]
fig, ax = plt.subplots(figsize=(12, 8)) # 设置画布
ax.scatter(positive['Exam1'], positive['Exam2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam1'], negative['Exam2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
plt.show()
# 数据处理
data.insert(0, 'Ones', 1) # 在原有数据基础上插入一列,令值全为1,名称为“Ones”,作为Xo
X = data.iloc[:, :3] # 切取前3列作为输入X
y = data.iloc[:, -1] # 切取最后一列作为输出y
theta = np.zeros(3) # 初始化模型参数
# 定义Sigmoid函数
def Sigmoid(z):
return 1 / (1 + np.exp(-z))
# 定义代价函数
def CostFunction(theta, X, y):
theta = np.mat(theta) # 将X,y,theta都变成矩阵形式
X = np.mat(X)
y = np.mat(y)
y = y.T
first = np.multiply(-y, np.log(Sigmoid(X * theta.T))) # 定义损失函数的两个部分
second = np.multiply((1 - y), np.log(1 - Sigmoid(X * theta.T)))
return np.sum(first - second) / (len(X))
# 定义梯度下降,只迭代一次,求出各偏导即可
def GradientDescent(theta, X, y):
theta = np.mat(theta) # 将X,y,theta都变成矩阵形式
X = np.mat(X)
y = np.mat(y)
y = y.T
grad = np.zeros(3) # grad用来存储各参数的偏导数
error = Sigmoid(X * theta.T) - y
for i in range(3):
term = np.multiply(error, X[:, i])
grad[i] = np.sum(term) / len(X)
return grad
# 利用高级优化算法,result结果的第一个参数就是训练好的模型参数
result = opt.fmin_tnc(func=CostFunction, x0=theta, fprime=GradientDescent, args=(X, y))
print(result)
print("优化好的参数为: ", result[0])
# 根据梯度下降得到的参数画出拟合模型
x = np.linspace(data.Exam1.min(), data.Exam1.max(), 100)
f = (-result[0][0] - result[0][1]*x)/result[0][2] # 判定边界就是h=0的曲线,令h=0画出曲线
fig, ax = plt.subplots(figsize=(12, 8))
ax.plot(x, f, 'r', label='Prediction')
ax.scatter(positive['Exam1'], positive['Exam2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam1'], negative['Exam2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
ax.set_title('Predicted IsAdmitted')
plt.show()
# 实现预测功能
def predict(theta, X):
theta = np.mat(theta)
probability = Sigmoid(theta*X)
return probability
X = np.mat([[1], [45], [85]])
print("第一门课45分,第二门课85分的学生被录取的概率为:", predict(result[0], X))
2. 正则化逻辑回归
在本练习的这一部分中,您将实施正则化逻辑回归,以预测来自制造工厂的微芯片是否通过质量保证。在QA期间,每个微芯片都经过各种测试以确保其正常运行。假设您是工厂的产品经理,并且您在两个不同的测试中获得了一些微芯片的测试结果。从这两个测试中,您想确定是应该接受还是拒绝微芯片。
2.1可视化数据
数据同样有3列,前两列代表微芯片测试的两个数据指标,最后一列代表是否接受,1表示接受,0表示拒绝。同样以散点图的形式将数据画出,方法同上。
# 画出散点图
path = r'C:\Users\Administrator\Desktop\ML\machine-learning-ex2\ex2\ex2data2.txt'
data = pd.read_csv(path, header=None, names=['Test1', 'Test2', 'Accepted'])
positive = data[data['Accepted'].isin([1])] # 利用isin()函数区分接受和拒绝的数据
negative = data[data['Accepted'].isin([0])]
fig, ax = plt.subplots(figsize=(12, 8)) # 设置画布
ax.scatter(positive['Test1'], positive['Test2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test1'], negative['Test2'], s=50, c='r', marker='x', label='Not Accepted')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.show()
运行结果:
2.2 特征映射
由散点图可以看出来,用线性决策边界不能够将数据分开,我们构造更高阶的多项式来实现数据的分开。
我们将最高次幂设置为6,有以下特征:
实现代码如下:
def feature_mapping(x1, x2):
for i in range(7):
for j in range(7-i):
data["f{}{}".format(i, j)] = np.power(x1, i) * np.power(x2, j)
return data
此时的data在原来三列的基础之上增加了28列新的特征,变成118行31列的,其中有两组Test1和Test2是重复的,我们先将它们提取出来给x1和x2,作为feature_mapping的输入,然后将它们从data中删除,通过drop()函数。
degree = 6 # 最高次幂设为6
x1 = data['Test1'] # 为x1和x2赋值
x2 = data['Test2']
feature_mapping(x1, x2) # 调用函数,实现特征扩充
data.drop('Test1', axis=1, inplace=True) # 删除重复的Test1和Test2
data.drop('Test2', axis=1, inplace=True)
此时data中的数据有29列,其中28列是输入特征,一列是输出y,我们可以看其中一部分:
Accepted f00 f01 ... f50 f51 f60
0 1 1.0 0.69956 ... 3.541519e-07 2.477505e-07 1.815630e-08
1 1 1.0 0.68494 ... -6.860919e-06 -4.699318e-06 6.362953e-07
2 1 1.0 0.69225 ... -4.457837e-04 -3.085938e-04 9.526844e-05
3 1 1.0 0.50219 ... -7.415771e-03 -3.724126e-03 2.780914e-03
4 1 1.0 0.46564 ... -3.561597e-02 -1.658422e-02 1.827990e-02
将data的数据拆分,特征赋给x,输出赋给y
cols = data.shape[1] # 为X和y赋值
X = data.iloc[:, 1:cols]
y = data.iloc[:, 0]
X = np.array(X.values) # 转换为array形式
y = np.array(y.values)
theta = np.zeros(28) # theta赋初值
2.3 成本函数和梯度
这时候,数据部分处理完,接下来定义代价函数和梯度下降算法:
# 定义Sigmod函数
def Sigmoid(z):
return 1/(1 + np.exp(-z))
# 定义代价函数
def CostFunction(theta, X, y, lr):
theta = np.mat(theta)
X = np.mat(X)
y = np.mat(y)
y = y.T
first = np.multiply(-y, np.log(Sigmoid(X * theta.T)))
second = np.multiply((1 - y), np.log(1 - Sigmoid(X * theta.T)))
reg = (lr / (2 * len(X))) * np.sum(np.power(theta[:, 1:theta.shape[1]], 2)) # 正则化项,theta0不进行正则化
return np.sum(first - second) / len(X) + reg
# 定义梯度下降
def GradientDescent(theta, X, y, lr):
theta = np.mat(theta)
X = np.mat(X)
y = np.mat(y)
y = y.T
parameters = int(theta.shape[1])
grad = np.zeros(parameters) # grad用来存储各参数的偏导数
error = Sigmoid(X * theta.T) - y
for i in range(parameters):
term = np.multiply(error, X[:, i])
if (i == 0): # theta0不需要正则化,需另外讨论
grad[i] = np.sum(term) / len(X)
else:
grad[i] = (np.sum(term) / len(X)) + ((lr / len(X)) * np.sum(theta[1:parameters]))
return grad
lr = 1 # 设置正则化的参数为1
print(CostFunction(theta, X, y, lr))
print(GradientDescent(theta, X, y, lr))
运行结果:
0.6931471805599454
[8.47457627e-03 7.77711864e-05 3.76648474e-02 2.34764889e-02
3.93028171e-02 3.10079849e-02 3.87936363e-02 1.87880932e-02
1.15013308e-02 8.19244468e-03 3.09593720e-03 4.47629067e-03
1.37646175e-03 5.03446395e-02 7.32393391e-03 1.28600503e-02
5.83822078e-03 7.26504316e-03 1.83559872e-02 2.23923907e-03
3.38643902e-03 4.08503006e-04 3.93486234e-02 4.32983232e-03
6.31570797e-03 1.99707467e-02 1.09740238e-03 3.10312442e-02]
使用与未使用正则化相同的高级优化算法拟合参数:
result = opt.fmin_tnc(func=CostFunction, x0=theta, fprime=GradientDescent, args=(X, y, lr))
print(result)
运行结果:
(array([ 0.0502174 , 0.06132202, -0.05997024, 0.00143168, -0.04668573,
-0.00804794, -0.02907847, 0.03612559, -0.05178206, -0.02232713,
-0.01567085, -0.01560254, -0.00682616, -0.09533275, -0.02129789,
-0.03312585, -0.01489697, -0.01696055, 0.01781899, -0.00610741,
-0.0050665 , -0.00124322, -0.0641886 , -0.01173556, -0.01623309,
0.00277193, 0.00025151, -0.03916331]), 92, 4)
2.4 绘制决策边界
根据优化好的参数将决策边界绘出
u = np.linspace(-1, 1.5, 50)
v = np.linspace(-1, 1.5, 50)
z = np.zeros((50, 50))
for i in range(50):
for j in range(50):
z[i, j] = np.sum(np.multiply(np.array(f(u[i], v[j])), np.array(result[0])))
path = r'C:\Users\Administrator\Desktop\ML\machine-learning-ex2\ex2\ex2data2.txt' # 重新读入数据
data = pd.read_csv(path, header=None, names=['Test1', 'Test2', 'Accepted'])
positive = data[data['Accepted'].isin([1])] # 利用isin()函数区分接受和拒绝的数据
negative = data[data['Accepted'].isin([0])]
fig, ax = plt.subplots(figsize=(12, 8)) # 设置画布
ax.scatter(positive['Test1'], positive['Test2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test1'], negative['Test2'], s=50, c='r', marker='x', label='Not Accepted')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.contour(u, v, z, 0) # 用画等高线图的方法绘出曲线
plt.show()
运行结果:
绘制边界曲线这边花的时间最长,主要是不太熟悉高阶多项式的绘画方法,最后绘制出来的边界曲线和其他人画的也有些不同,希望看出问题的能够指出,谢谢!
完整代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.optimize as opt
# 画出散点图
path = r'C:\Users\Administrator\Desktop\ML\machine-learning-ex2\ex2\ex2data2.txt'
data = pd.read_csv(path, header=None, names=['Test1', 'Test2', 'Accepted'])
positive = data[data['Accepted'].isin([1])] # 利用isin()函数区分接受和拒绝的数据
negative = data[data['Accepted'].isin([0])]
fig, ax = plt.subplots(figsize=(12, 8)) # 设置画布
ax.scatter(positive['Test1'], positive['Test2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test1'], negative['Test2'], s=50, c='r', marker='x', label='Not Accepted')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.show()
def feature_mapping(x1, x2):
for i in range(7):
for j in range(7-i):
data["f{}{}".format(i, j)] = np.power(x1, i) * np.power(x2, j)
return data
def f(x1, x2):
count = 0
y = []
for i in range(7):
for j in range(7-i):
y.insert(count, np.power(x1, i) * np.power(x2, j))
count += 1
return y
# 定义Sigmod函数
def Sigmoid(z):
return 1/(1 + np.exp(-z))
# 定义代价函数
def CostFunction(theta, X, y, lr):
theta = np.mat(theta)
X = np.mat(X)
y = np.mat(y)
y = y.T
first = np.multiply(-y, np.log(Sigmoid(X * theta.T)))
second = np.multiply((1 - y), np.log(1 - Sigmoid(X * theta.T)))
reg = (lr / (2 * len(X))) * np.sum(np.power(theta[:, 1:theta.shape[1]], 2)) # 正则化项,theta0不进行正则化
return np.sum(first - second) / len(X) + reg
# 定义梯度下降
def GradientDescent(theta, X, y, lr):
theta = np.mat(theta)
X = np.mat(X)
y = np.mat(y)
y = y.T
parameters = int(theta.shape[1])
grad = np.zeros(parameters) # grad用来存储各参数的偏导数
error = Sigmoid(X * theta.T) - y
for i in range(parameters):
term = np.multiply(error, X[:, i])
if (i == 0): # theta0不需要正则化,需另外讨论
grad[i] = np.sum(term) / len(X)
else:
grad[i] = (np.sum(term) / len(X)) + ((lr / len(X)) * theta[: i]
return grad
# 设置特征变量
degree = 6 # 最高次幂设为6
x1 = data['Test1'] # 为x1和x2赋值
x2 = data['Test2']
feature_mapping(x1, x2) # 调用函数,实现特征扩充
data.drop('Test1', axis=1, inplace=True) # 删除重复的Test1和Test2
data.drop('Test2', axis=1, inplace=True)
cols = data.shape[1] # 为X和y赋值
X = data.iloc[:, 1:cols]
y = data.iloc[:, 0]
X = np.array(X.values) # 转换为array形式
y = np.array(y.values)
theta = np.zeros(28) # theta赋初值
lr = 1 # 设置正则化的参数为1
print(CostFunction(theta, X, y, lr))
print(GradientDescent(theta, X, y, lr))
result = opt.fmin_tnc(func=CostFunction, x0=theta, fprime=GradientDescent, args=(X, y, lr))
print(result)
u = np.linspace(-1, 1.5, 50)
v = np.linspace(-1, 1.5, 50)
z = np.zeros((50, 50))
for i in range(50):
for j in range(50):
z[i, j] = np.sum(np.multiply(np.array(f(u[i], v[j])), np.array(result[0])))
path = r'C:\Users\Administrator\Desktop\ML\machine-learning-ex2\ex2\ex2data2.txt' # 重新读入数据
data = pd.read_csv(path, header=None, names=['Test1', 'Test2', 'Accepted'])
positive = data[data['Accepted'].isin([1])] # 利用isin()函数区分接受和拒绝的数据
negative = data[data['Accepted'].isin([0])]
fig, ax = plt.subplots(figsize=(12, 8)) # 设置画布
ax.scatter(positive['Test1'], positive['Test2'], s=50, c='b', marker='o', label='Accepted')
ax.scatter(negative['Test1'], negative['Test2'], s=50, c='r', marker='x', label='Not Accepted')
ax.legend()
ax.set_xlabel('Test 1 Score')
ax.set_ylabel('Test 2 Score')
plt.contour(u, v, z, 0) # 用画等高线图的方法绘出曲线
plt.show()