基于逻辑回归算法实现乳腺癌识别
本篇文章将会设计并实现一个乳腺癌识别算法。
首先让我们了解完成这项任务所需的技术基础是什么?明确算法的输入与输出要求是关键。所采用的技术体系是什么样的?整个流程的设计与实现过程是怎样的呢?本文使用sklearn.datasets下的乳腺癌数据集。通过对该数据集进行深入分析后发现这是一个经典的二分类问题吗?由于样本数量有限我们需要选择合适的模型以避免出现过拟合现象吗?在下文中我们将介绍并实现a standard logistic regression model来解决这个问题吗?
Logistic算法具有高效计算能力和简便操作特点,在工业领域得到了广泛应用。例如,在推荐系统和广告投放系统等方面均展现出显著优势。然而该方法对于输入数据中的特征具有较高的依赖性——这些特征直接影响模型的最大性能。通常伴随着复杂的特征工程工作,在本文中我们将深入探讨该算法的基本理论框架,并通过实际案例展示其在乳腺癌诊断中的应用效果。通过实验验证表明,在该应用场景下Logistic算法能够达到令人满意的分类效果
01
数据集加载
请参考代码列表14-1所示的具体实现方式,并且该数据集将按照比例划分出4份用于训练以及剩余的1份用于测试。
代码清单 14-1 数据集加载函数
def loadTrainData():
cancer = load_breast_cancer() # 加载乳腺癌数据
X = cancer.data # 加载乳腺癌判别特征
y = cancer.target # 两个TAG,y = 0时为阴性,y = 1时为阳性
# 将数据集划分为训练集和测试集,测试集占比为0.2
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
X_train = X_train.T
X_test = X_test.T
return X_train, X_test, y_train, y_test
代码解读
02
Logistic模块
本模块包含两个主要功能:通过正向传播算法计算出预测结果,并通过整体优化机制确保模型参数得到更新。其中第一个功能是在代码清单14-2中实现了sigmoid函数。
代码清单 14-2 sigmoid函数
def sigmoid(inx):
from numpy import exp
return 1.0/(1.0 + exp(-inx))
代码解读
如代码清单 14-3所示,在本文中使用基于随机高斯函数的立即初始化方法来设定变量w的值,并将参数b预先设为零值。
代码清单 14-3 参数初始化函数
# 初始化参数
def initialize_para(dim):
mu = 0
sigma = 0.1
np.random.seed(0)
w = np.random.normal(mu, sigma, dim)
w = np.reshape(w, (dim, 1))
b = 0
return w, b
代码解读
参考代码清单14-4中描述了前馈传播过程的相关细节:该过程采用参数w和b以及训练数据集X与标签Y作为输入参数,并通过一系列运算计算出预测输出A以及损失函数对权重参数w和偏置项b的梯度值,并最终返回这些梯度信息。需要注意的是,在实际计算过程中可能会出现对数函数处理输入数值为零的情况(即log(0)),这会导致程序出现异常情况。因此建议在执行此类数值运算时设定一个极小的正数eps(epsilon),并在相关运算中自动加至被取对数值域中以避免计算异常问题。
代码清单 14-4 前向传播函数
# 前向传播
def propagate(w, b, X, Y):
# eps防止log运算遇到0
eps = 1e-5
m = X.shape[1]
# 计算初步运算结果
A = sigmoid(np.dot(w.T, X) + b)
# 计算损失函数值大小
cost = -1 / m * np.sum(np.multiply(Y, np.log(A + eps)) + np.multiply(1 - Y, np.log(1 - A + eps)))
# 计算梯度值
dw = 1 / m * np.dot(X, (A - Y).T)
db = 1 / m * np.sum(A-Y)
cost = np.squeeze(cost)
grads = {"dw": dw,
"db": db}
# 返回损失函数大小以及反向传播的梯度值
return grads, cost, A
代码解读
如代码清单 14-5所示,在训练集上对模型进行持续优化是该核心函数的主要职责。该函数接收输入变量w和b、训练样本集X与Y以及与参数更新相关的学习率和迭代次数等关键参数,在完成100次迭代之前即可评估当前模型在训练集上的性能表现及其损失函数变化趋势。等到所有参数更新完成时,并返回模型参数及其损失函数变化情况。
代码清单 14-5 参数优化函数
# num_iterations 梯度下降次数
# learning_rate 学习率
def optimize(w, b, X, Y, num_iterations, learning_rate):
costs = [] # 记录损失函数值
# 循环进行梯度下降
for i in range(num_iterations):
# print(i)
grads, cost, pre_Y = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
w = w - learning_rate * dw
b = b - learning_rate * db
# 每100次循环记录一次损失函数大小并打印
if i % 100 == 0:
costs.append(cost)
if i % 100 == 0:
pre_Y[pre_Y >= 0.5] = 1
pre_Y[pre_Y < 0.5] = 0
pre_Y = pre_Y.astype(np.int)
acc = 1 - np.sum(pre_Y ^ Y) / len(Y)
print("Iteration:{} Loss = {}, Acc = {}".format(i, cost, acc))
# 最终参数值
params = {"w": w,
"b": b}
return params, costs
代码解读
在训练完成后,为了使用该模型对其他输入进行预测而设计的 predict 函数得以实现。该函数基于参数 w 和 b,并以输入数据 X 作为其输入参数运行后会输出预测结果。如代码清单 14-6所示。
代码清单 14-6 预测结果函数
def predict(w, b, X):
# 样本个数
m = X.shape[1]
# 初始化预测输出
Y_prediction = np.zeros((1, m))
# 转置参数向量w
w = w.reshape(X.shape[0], 1)
# 预测结果
Y_hat = sigmoid(np.dot(w.T, X) + b)
# 将结果按照0.5的阈值转化为0/1
for i in range(Y_hat.shape[1]):
if Y_hat[:, i] > 0.5:
Y_prediction[:, i] = 1
else:
Y_prediction[:, i] = 0
return Y_prediction
代码解读
03
模型评价
最终实现了在训练集进行训练,并在测试集上进行评估Logistic模型的功能。该功能的具体实现细节可见于附录中的代码清单14-8部分。研究团队特别设定了解决方案中的迭代次数为1000次,并将学习率固定为0.2这一参数值。同时编写了如代码清单14-7所示的main函数作为整个脚本的主要入口。
代码清单 14-7 主函数
if __name__ == '__main__':
X_train, X_test, y_train, y_test = loadTrainData()
Logisticmodel(X_train, y_train, X_test, y_test)
代码解读
代码清单 14-8 训练集预测函数
# 训练以及预测
def Logisticmodel(X_train, Y_train, X_test, Y_test, num_iterations=1000, learning_rate=0.1):
# 初始化参数w,b
w, b = initialize_para(X_train.shape[0])
# 梯度下降找到最优参数
parameters, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate)
w = parameters["w"]
b = parameters["b"]
# 训练集测试集的预测结果
Y_prediction_train = predict(w, b, X_train)
Y_prediction_test = predict(w, b, X_test)
Y_prediction_test = Y_prediction_test.T
# 模型评价
accuracy_score_value = accuracy_score(Y_test, Y_prediction_test)
recall_score_value = recall_score(Y_test, Y_prediction_test)
precision_score_value = precision_score(Y_test, Y_prediction_test)
classification_report_value = classification_report(Y_test, Y_prediction_test)
print("准确率:", accuracy_score_value)
print("召回率:", recall_score_value)
print("精确率:", precision_score_value)
print(classification_report_value)
d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train": Y_prediction_train,
"w": w,
"b": b,
"learning_rate": learning_rate,
"num_iterations": num_iterations}
return d
代码解读
运行整个代码,可以得到如图 14-1的输出。

图 14-1 执行代码命令行输出
当迭代次数达到约400次时
经过测试后验证模型的准确率达到约92%,其表现与训练集相当程度上一致,并未出现过拟合现象。
如果将学习率进一步下调到0.1,会产生什么样的结果?
小技巧
小技巧
实用技巧

图 14-2 学习率下降后执行代码命令行输出
如图所示,在观察到学习率下降的现象后
