机器学习案例4:基于逻辑回归和梯度下降的肿瘤预测

案例4:基于逻辑回归和梯度下降的肿瘤预测

为什么写本博客

​ 前人种树,后人乘凉。希望自己的学习笔记可以帮助到需要的人。

需要的基础

​ 懂不懂原理不重要,本系列的目标是使用python实现机器学习。

​ 必须会的东西:python基础、numpy、pandas、matplotlib和库的使用技巧。

说明

​ 完整的代码在最后,另外之前案例中出现过的方法不会再讲解。

目录结构

1. 说明:

​ 在前一篇文章中,我们使用了sklearn直接调用逻辑回归算法来实现模型,为了体现出梯度下降算法的重要性,这里我补充一篇使用梯度下降来实现上一篇的案例。

2. 数据集介绍、划分与处理:

​ 为了方便大家不去看上一篇文章,这里我把之前的内容拷贝了下来。

数据集介绍

​ 这次使用的数据需要在网上下载,下载到本地的data文件夹内,代码文件就在data文件夹外,与之同级别,网址为https://archive.ics.uci.edu/ml/machine-learning-databases/,我们使用的数据为:

在这里插入图片描述

​ 其中,后缀为data的为数据文件,names对属性进行说明的文件。

​ 这个**数据共699条,共9个特征,一个索引id(第一列),一个肿瘤类型标签(最后一列)**具体的数据内容为:

1000025,5,1,1,1,2,1,3,1,1,2
# 索引id(没啥用),除去最后一列外的为肿瘤特征,最后一列为肿瘤的类型值

这个数据具有缺失值,缺失值使用?表示,缺失值共16条

加载

​ 这里,我们使用pandas来加载数据,首先定义一个列表,其内容为数据的标签:

# 定义标签
names = ['Sample code number', 'Clump Thickness', 'Uniformity of Cell Size', 'Uniformity of Cell Shape',
                   'Marginal Adhesion', 'Single Epithelial Cell Size', 'Bare Nuclei', 'Bland Chromatin',
                   'Normal Nucleoli', 'Mitoses', 'Class']

​ 接着,使用pandas加载数据并显示前五条数据:

# 导入数据
data = pd.read_csv('./data/breast-cancer-wisconsin.data',names=names)
print(data.head())
print(data.shape) # (699, 11)

​ 结果如下:

   Sample code number  Clump Thickness  ...  Mitoses  Class
0             1000025                5  ...        1      2
1             1002945                5  ...        1      2
2             1015425                3  ...        1      2
3             1016277                6  ...        1      2
4             1017023                4  ...        1      2
(699, 11)

处理

​ 下面,我们对缺失值进行处理,这里我们采取的简单处理,就是丢弃相关的数据即可:

# 缺失值丢失
data = data.replace(to_replace='?',value=np.NaN)
data = data.dropna()
print(data.shape) # (683, 11)

​ 然后,我们需要把数据分开一下,即将特征提取出来,把标签页提取出来:

# 数据处理
x = data.iloc[:,1:10]
y = data.iloc[:,10]

​ 然后,对数据集进行切分,按照8:2的比列分为训练集和测试集:

# 数据分割
x_train,x_test,y_train,y_test = model_selection.train_test_split(x,y,test_size=0.2,random_state=20)
print(x_train.shape) # (546, 9)
print(y_train.shape) #(546,)

​ 最后,将数据进行标准化即可:

# 标准化
standard = preprocessing.StandardScaler()
x_train = standard.fit_transform(x_train)
x_test = standard.fit_transform(x_test)

3. 梯度下降算法实现:

​ 首先,我们需要定义一个sigmoid函数,这个很简单,根据函数定义来写代码即可:

# 定义sigmoid函数
def sigmoid(x):
    return 1.0/(1+np.exp(-x))

​ 接下来,定义梯度下降函数,这里采取批量梯度下降算法。

​ 首先,定义一个函数,参数为传入的数据和标签值,并定义一些简单的数据(看注释):

# 定义梯度函数
def gradAscent(data, classes):
	m,n = np.shape(data)  # m:行;n:列
    alpha = 0.001     # 学习率
    max_iter_num = 5000   # 最大迭代次数

​ 接下来,需要知道逻辑回归中对损失函数求导后的公式,即:

dL / dw = X(f(x) - y)
其中:
	X 为输入数据
	f(x) 为sigmoid函数计算值
	y 为 真实标签值

​ 基于上述公式,我们可以继续构建我们需要的数据:

# 定义梯度函数
def gradAscent(data, classes):
	m,n = np.shape(data)  # m:行;n:列
    alpha = 0.001     # 学习率
    max_iter_num = 5000   # 最大迭代次数
    weights = np.ones((n,1)) # 构建一个权值矩阵,主要用来容纳我们的w值
    for k in range(max_iter_num): # 开始进行迭代
    	h = sigmoid(np.dot(data,weights))   # 计算sigmoid值,即sigmoid(x),其中x=Xw
        h = h.reshape(546,)	# 原来得到的是(546,1),为了与label(546,)计算,需要修改
        temp = h - labels # 就是逻辑回归中的f(x) - y
        weights = weights - alpha * (np.dot(data.T,temp).reshape(9,1)) # 这里需要转为9,1是因为得到的结果为(9,),因此为可以和weights相减,需要改变
    return weights

4. 模型训练和评估:

​ 下面,我们进行模型的训练:

weights = gradAscent(x_train,y_train)
print(weights)	# 打印参数值

​ 打印的结果如下:

[[ 95.2765168 ]
 [110.45832716]
 [109.41962161]
 [ 90.88637168]
 [ 89.3050441 ]
 [109.05910826]
 [ 98.12410698]
 [ 93.75179632]
 [ 59.0753551 ]]

​ 虽然权值看起来有点离谱,但是我们需要用准确率来验证一下代码是否有误:

# 预测值
pred = sigmoid(np.dot(x_test,weights)).reshape(137,)
# 不转为的结果为(137,1),而转为(137,)是为了方便后面与真实标签(137,)进行比较

​ 下面,进行比较,首先对预测的结果进行处理,需要将数值转为标签值,而查看数据文档可以知道,肿瘤分为两类,即2和4:

# 存放预测标签的地方
label = []
for i in pred:
    # 由于为sigmoid函数,因此认为大于0.5为一类。
    if i > 0.5:
        label.append(4)
    else:
        label.append(2)

​ 接下来,进行准确率计算:

# 计算分类准确的样本数目
count = 0
for pred,true in zip(label,y_test):
    if pred == true:
        count +=1
print('准确率:',count/len(label))

​ 运行结果为;

准确率: 0.9562043795620438

​ 根据上一讲的内容,可以知道使用sklearn调用的结果也是:

准确率: 0.9562043795620438

​ 因此,可以知道代码书写正确。

5. 总结:

​ 自己来实现的确有点麻烦,不过麻烦之处不在于写代码的思路上,因为思路主要熟悉原理就知道,主要涉及到矩阵形式的变换,因为有时候得出的结果是(xxx,1),有时候为(xxx,),虽然两者的值相同,但是前者为二维数据,后者为一维数据,因此涉及到转换。

​ 另外,自己实现梯度下降也是很有必要的,当你写神经网络代码的时候,也是需要自己实现梯度下降算法,至少这里熟悉一次,后面会简单一些。

​ 最后,建议大家可以找那种二维的数据,来实现梯度下降,这样可以实现代数的运算,因为我这里用的矩阵形式。

完整代码

# author: baiCai
# 导入包
import sklearn
import numpy as np
import pandas as pd
from sklearn import model_selection
from sklearn import preprocessing
from sklearn.linear_model import LogisticRegression

# 定义标签
names = ['Sample code number', 'Clump Thickness', 'Uniformity of Cell Size', 'Uniformity of Cell Shape',
                   'Marginal Adhesion', 'Single Epithelial Cell Size', 'Bare Nuclei', 'Bland Chromatin',
                   'Normal Nucleoli', 'Mitoses', 'Class']
# 导入数据
data = pd.read_csv('./data/breast-cancer-wisconsin.data',names=names)
# print(data.head())
# print(data.shape) # (699, 11)
# 缺失值丢失
data = data.replace(to_replace='?',value=np.NaN)
data = data.dropna()
# print(data.shape) # (683, 11)
# 数据处理
x = data.iloc[:,1:10]
y = data.iloc[:,10]
# 数据分割
x_train,x_test,y_train,y_test = model_selection.train_test_split(x,y,test_size=0.2,random_state=20)
# print(x_train.shape) # (546, 9)
# print(y_train.shape) #(546,)
# 标准化
standard = preprocessing.StandardScaler()
x_train = standard.fit_transform(x_train)
x_test = standard.fit_transform(x_test)

# 定义sigmoid函数
def sigmoid(x):
    return 1.0/(1+np.exp(-x))
# 定义梯度函数
def gradAscent(data, labels):
    m,n = np.shape(data)  # m:行;n:列
    alpha = 0.001     # 学习率
    max_iter_num = 500   # 最大迭代次数
    weights = np.ones((n,1)) # 构建一个权值矩阵,主要用来容纳我们的w值
    for k in range(max_iter_num): # 开始进行迭代
        h = sigmoid(np.dot(data,weights))   # 计算sigmoid值,即sigmoid(x),其中x=Xw
        h = h.reshape(546,)
        temp = h - labels
        weights = weights - alpha * (np.dot(data.T,temp).reshape(9,1))
    return weights
# 模型训练
weights = gradAscent(x_train,y_train)
# print(weights)
# 预测值
pred = sigmoid(np.dot(x_test,weights)).reshape(137,)
# print(pred)
# 存放预测标签的地方
label = []
for i in pred:
    # 由于为sigmoid函数,因此认为大于0.5为一类。
    if i > 0.5:
        label.append(4)
    else:
        label.append(2)
# 计算分类准确的样本数目
count = 0
for pred,true in zip(label,y_test):
    if pred == true:
        count +=1
print('准确率:',count/len(label))
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值