XDU机器学习第二次作业
目录
使用scipy.optimize.fmin_cg学习模型参数
一、逻辑回归
本次作业的目的是建立一个逻辑回归模型,用于预测一个学生是否应该被大学录取。
简单起见,大学通过两次考试的成绩来确定一个学生是否应该录取。你有以前数届考生的成绩,可以做为训练集学习逻辑回归模型。每个训练样本包括了考生两次考试的成绩和对应的录取决定。
你的任务是建立一个分类模型,根据两次考试的成绩来估计考生被录取的概率。 本次实验需要实现的函数
# 导入需要用到的库
import numpy as np
import scipy.optimize as op
import matplotlib.pyplot as plt
数据可视化
在实现机器学习算法前,可视化的显示数据以观察其规律通常是有益的。本次作业中,你需要实现 plot_data
函数,用于绘制所给数据的散点图。你绘制的图像应如下图所示,两坐标轴分别为两次考试的成绩,正负样本分别使用不同的标记显示。
def plot_data(X, y):
"""This function plots the data points X and y into a new figure.
It plots the data points with red + for the positive examples,
and blue o the negative examples. X is assumed to be a Mx2 matrix.
X: shape:nx2
y: shape:nx1
"""
plt.figure()
# ====================== YOUR CODE HERE ======================
ax = list()
ay = list()
bx = list()
by = list()
for i in range(len(y)):
if y[i] == 0:
ax.append(X[i][0])
ay.append(X[i][1])
else:
bx.append(X[i][0])
by.append(X[i][1])
plt.plot(ax, ay, 'r+', label="Not admitted")
plt.plot(bx, by, 'bo', label='Admitted')
# ============================================================
plt.xlabel("Exam 1 Score")
plt.ylabel("Exam 2 Score")
plt.legend(loc='upper right')
调用 plot_data
,可视化第一个文件LR_data1
数据。绘制的图像如下:
# 加载数据 注意使用 !ls 或 !find 命令确定数据文件所在的目录 dataXXXX 。
data = np.loadtxt("LR_data1.txt", delimiter=",")
X, y = data[:, :2], data[:, 2]
# 可视化数据
# ====================== YOUR CODE HERE ================
plot_data(X, y)
# ======================================================
plt.show()
运行结果:
绘制分类面:
def plot_decision_boundary(theta, X, y):
"""绘制分类面。"""
plot_data(X[:, 1:], y)
_, d = X.shape
if d <= 3:
plot_x = np.array([np.min(X[:, 1])-2, np.max(X[:, 1])+2])
plot_y = -1.0 / theta[2]*(theta[1]*plot_x + theta[0])
plt.plot(plot_x, plot_y, 'm-', label="Decision Boundary")
plt.xlim([30, 100])
plt.ylim([30, 100])
else:
n_grid = 50
u = np.linspace(-1, 1.5, n_grid)
v = np.linspace(-1, 1.5, n_grid)
z = np.zeros((n_grid, n_grid))
for i in range(n_grid):
for j in range(n_grid):
uu, vv = np.array([u[i]]), np.array([v[j]])
z[i, j] = np.dot(map_feature(uu, vv), theta)
z = z.T
CS = plt.contour(u, v, z, linewidths=2, levels=[0.0], colors=['m'])
CS.collections[0].set_label('Decision boundary')
plt.legend()
热身练习:Sigmoid函数
逻辑回归的假设模型为:
其中函数 g(⋅)g(\cdot)g(⋅) 是Sigmoid函数,定义为:
本练习中第一步需要你实现 Sigmoid 函数。在实现该函数后,你需要确认其功能正确。对于输入为矩阵和向量的情况,你实现的函数应当对每一个元素执行Sigmoid 函数。
def sigmoid(z):
"""Compute sigmoid function"""
z = np.asarray(z)
g = np.zeros_like(z)
# ====================== YOUR CODE HERE ======================
g = 1/(1+np.exp(-z))
# ============================================================
return g
# 测试 sigmoid 函数
z = np.array([-10.0, -5.0, 0.0, 5.0, 10.0])
g = sigmoid(z)
print("Value of sigmoid at [-10, -5, 0, 5, 10] are:\n", g)
运行结果:
代价函数与梯度
现在你需要实现逻辑回归的代价函数及其梯度。补充完整cost_function
函数,使其返回正确的代价。补充完整cost_gradient
函数,使其返回正确的梯度。
逻辑回归的代价函数为:
对应的梯度向量各分量为:
def cost_function(theta, X, y):
"""逻辑回归的代价函数,无正则项。"""
J = 0.0
# ====================== YOUR CODE HERE ======================
J = (-y*np.log(sigmoid(np.dot(X,theta.T))) - (1-y)*np.log(1-sigmoid(np.dot(X,theta.T))))
J = np.mean(J, axis=0)
# ============================================================
return J
预测函数
在获得模型参数后,你就可以使用模型预测一个学生能够被大学录取。如果某学生考试一的 成绩为45,考试二的成绩为85,你应该能够得到其录取概率约为0.776。
你需要完成 predict
函数,该函数输出“1”或“0”。通过计算分类正确的样本百分数, 我们可以得到训练集上的正确率。
def predict(theta, X):
"""Predict whether the label is 0 or 1
using learned logistic regression parameters theta.
input: theta:model's parameters
X: input samples
output:0 or 1
"""
m, _ = X.shape
pred = np.zeros((m, 1), dtype=np.bool)
# ====================== YOUR CODE HERE ======================
pred = (np.dot(X, theta.T) > 0.5)
# ============================================================
return pred
使用scipy.optimize.fmin_cg
学习模型参数
在本次作业中,希望你使用 scipy.optimize.fmin_cg
函数实现代价函数 J(θ)J(\theta)J(θ) 的优化,得到最佳参数 θ∗\theta^{*}θ∗ 。
使用该优化函数的代码已经在程序中实现,调用方式示例如下:
其中cost_function
为代价函数, theta
为需要优化的参数初值, fprime=cost_gradient
给出了代价函数的梯度, args=(X, y)
给出了需要优化的函数与对应的梯度计算所需要的其他参数, maxiter=400
给出了最大迭代次数, full_output=True
则指明该函数除了输出优化得到的参数 theta_opt
外,还会返回最小的代价函数值 cost_min
等内容。
对第一组参数,得到的代价约为 0.203 (cost_min)。
def logistic_regression():
"""针对第一组数据建立逻辑回归模型。"""
# 加载数据
data = np.loadtxt("LR_data1.txt", delimiter=",")
X, y = data[:, :2], data[:, 2]
# 计算代价与梯度
m, _ = X.shape
X = np.hstack((np.ones((m, 1)), X))
# 初始化参数
theta_initial = np.zeros_like(X[0])
# 计算并打印初始参数对应的代价与梯度
cost = cost_function(theta_initial, X, y)
grad = cost_gradient(theta_initial, X, y)
print("Cost at initial theta (zeros): ", cost)
print("Gradient at initial theta (zeros): \n", grad)
# 使用 scipy.optimize.fmin_cg 优化模型参数
args = (X, y)
maxiter = 200
# ====================== YOUR CODE HERE ======================
ret = op.fmin_cg(cost_function,
theta_initial,
fprime=cost_gradient,
args=(X, y),
maxiter=400,
full_output=True)
# ============================================================
theta_opt, cost_min, _, _, _ = ret
print("Cost at theta found by fmin_cg: ", cost_min)
print("theta_op: \n", theta_opt)
# 绘制分类面
plot_decision_boundary(theta_opt, X, y)
plt.show()
# 预测考试一得45分,考试二得85分的学生的录取概率
x_test = np.array([1, 45, 85.0])
prob = sigmoid(np.dot(theta_opt, x_test))
print('For a student with scores 45 and 85, we predict an admission probability of: ', prob)
# 计算在训练集上的分类正确率
p = predict(theta_opt, X)
print("Train Accuracy: ", np.mean(p == y)*100.)
logistic_regression()
运行结果:
二、正则化的逻辑回归:
数据可视化
调用函数plot_data
可视化第二组数据 LR_data2.txt
。 正确的输出如下:
# 加载数据
data = np.loadtxt("LR_data2.txt", delimiter=",")
X, y = data[:, :2], data[:, 2]
# 可视化数据
# ====================== YOUR CODE HERE ================
plot_data(X, y)
# ======================================================
plt.show()
运行结果:
特征变换
创建更多的特征是充分挖掘数据中的信息的一种有效手段。在函数 map_feature 中,我们将数据映射为其六阶多项式的所有项。
def map_feature(X1, X2, degree=6):
"""Feature mapping function to polynomial features."""
m = len(X1)
assert len(X1) == len(X2)
n = int((degree+2)*(degree+1)/2)
out = np.zeros((m, n))
idx = 0
for i in range(degree+1):
for j in range(i+1):
# print i-j, j, idx
out[:, idx] = np.power(X1, i-j)*np.power(X2, j)
idx += 1
return out
代价函数与梯度
逻辑回归的代价函数为:
对应的梯度向量各分量为:
完成以下函数:
cost_function_reg()
cost_gradient_reg()
def cost_function_reg(theta, X, y, lmb): """逻辑回归的代价函数,有正则项。""" m = 1.0*len(y) J = 0 # ====================== YOUR CODE HERE ====================== hx = np.log(sigmoid(np.dot(X,theta.T))) J = np.mean(-y*np.log(hx) - (1-y)*np.log(1-hx)) + \ lmb/(2*m) * (theta**2).sum() # ============================================================ return J def cost_gradient_reg(theta, X, y, lmb): """逻辑回归的代价函数的梯度,有正则项。""" m = 1.0*len(y) grad = np.zeros_like(theta) # ====================== YOUR CODE HERE ====================== hx = np.log(sigmoid(np.dot(X,theta.T))) theta[0] = np.mean((hx-y)*X[:,0]) temp = theta[1:] temp = temp.reshape(len(theta)-1,1) #print(np.mean((hx-y)*X[:,1:]).shape) a = hx-y a = a.reshape(len(a),1) ''' print('a',a.shape) print('temp',temp.shape) print('lmb/',(lmb/m*temp).shape) print('X[:,1:]',X[:,1:].shape) print('np.mean(a*X[:,1:])',np.mean(a*X[:,1:]).shape) print((a*X[:,1:]).shape) ''' ccc = (np.mean(a*X[:,1:]) + lmb/m*temp).reshape((temp.shape[0])) #print('ccc',ccc.shape) #theta[1:] = np.mean(a*X[:,1:]) + lmb/m*temp theta[1:] = ccc # ============================================================ return grad
模型训练
如果将参数θ\thetaθ 初始化为全零值,相应的代价函数约为 0.693。可以使用与前述无正则化项类似的方法实现梯度下降, 获得优化后的参数 θ∗\theta^{*}θ∗ 。 你可以调用 plot_decision_boundary 函数来查看最终得到的分类面。建议你调整正则化项的系数,分析正则化对分类面的影响!
参考输出图像:
def logistic_regression_reg(lmb=1.0):
"""针对第二组数据建立逻辑回归模型。"""
# 加载数据
data = np.loadtxt("LR_data2.txt", delimiter=",")
X, y = data[:, :2], data[:, 2]
# 计算具有正则项的代价与梯度
# 注意map_feature会自动加入一列 1
X = map_feature(X[:, 0], X[:, 1])
print(X.shape)
# 初始化参数
theta_initial = np.zeros_like(X[0, :])
# 计算并打印初始参数对应的代价与梯度
cost = cost_function_reg(theta_initial, X, y, lmb=lmb)
grad = cost_gradient_reg(theta_initial, X, y, lmb=lmb)
print("Cost at initial theta (zeros): ", cost)
print("Gradient at initial theta (zeros): \n", grad)
# 使用 scipy.optimize.fmin_cg 优化模型参数
args = (X, y, lmb)
maxiter = 200
# ====================== YOUR CODE HERE ======================
ret = op.fmin_cg(cost_function,
theta_initial,
fprime=cost_gradient,
args=(X, y),
maxiter=500,
full_output=True)
# ============================================================
theta_opt, cost_min, _, _, _ = ret
print("Cost at theta found by fmin_cg: ", cost_min)
print("theta_op: \n", theta_opt)
# 绘制分类面
plot_decision_boundary(theta_opt, X, y)
plt.title("lambda = " + str(lmb))
plt.show()
# 计算在训练集上的分类正确率
pred = predict(theta_opt, X)
print("Train Accuracy: ", np.mean(pred == y)*100)
# 可选:尝试不同正则化系数lmb = 0.0, 1.0, 10.0, 100.0对分类面的影响
logistic_regression_reg(lmb=1.0)
运行结果: