知识点
- 数据集的标准化
- 数据集的划分
- Sigmoid 函数
- 乳腺癌的预测
数据集的预处理
import pandas as pd
df = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/2534/breast_cancer.csv', index_col=False)
df
可以看到该数据集合一共有 569 条数据,每条数据有 30 个和乳腺癌相关的病变特征,最后一列是该患者是否患有乳腺癌的诊断结果。其中 0 表示没有患有乳腺癌,1 表示患有乳腺癌。
我们可以利用 pandas 中的切片,先将上表中的特征和标签分开:获得numpy类型
X = df[df.columns[0:-1]].values
y = df[df.columns[-1]].values
X.shape, y.shape
#输出((569, 30), (569,))
type(X),type(y)
#输出(numpy.ndarray, numpy.ndarray)
可以看到共有 569 条数据,每条数据有 30 个特征和 1 个标签。
数据集的划分和标准化
为了能够评价模型的好坏,这里我们利用 sklearn.model_selection 函数,将原数据按比例随机分为训练数据集和测试数据集,如下:
from sklearn.model_selection import train_test_split
# 按照 0.8 和 0.2 的比例随机划分数据集合
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=1234)
X_train.shape, y_train.shape, X_test.shape, y_test.shape
#输出((455, 30), (455,), (114, 30), (114,))
为了加快模型的收敛速度,一般我们都需要对原始数据进行标准化处理,将所有的数据按照比例缩放到一定范围内。
这里我们可以使用 sklearn.preprocessing 来对数据集合进行标准化。
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
# 对特征进行标准化,标签不要标准化,因为标签只有 0 和 1
X_train = sc.fit_transform(X_train)
X_test = sc.transform(X_test)
X_train
最后,为了将数据放入 PyTorch 定义的模型之中,我们必须将所有的数据转为 张量类型:
import torch
import numpy as np
X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.astype(np.float32))
y_test = torch.from_numpy(y_test.astype(np.float32))
# 将标签也转为 2 维,否则放入模型之中训练时,可能出错
y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)
X_train.size(), y_train.size()
模型的定义
import torch.nn as nn
# 我们的模型是一个线性函数+激活函数的非线性模型
# modle(x) = sigmoid(w*x+b)
class Model(nn.Module):
def __init__(self, n_input_features):
super(Model, self).__init__()
self.linear = nn.Linear(n_input_features, 1)
def forward(self, x):
# torch 中已经定义了 sigmoid 函数模型
y_pred = torch.sigmoid(self.linear(x))
return y_pred
# 获得样本量和特征数
n_samples, n_features = X.shape
# 模型的初始化
model = Model(n_features)
model
#Model((linear): Linear(in_features=30, out_features=1, bias=True)
)
损失函数和优化器
首先,让我们来定义一下损失函数,由于我们的标签只有 0 和 1,因此这里使用二元交叉熵损失来计算真实值和预测值之间的距离了。该损失函数的公式如下:
当然,我们不必手写上面的损失函数, 直接使用 nn.BCELoss() 即可:
# 损失和优化器的定义
# 迭代次数
num_epochs = 100
# 学习率
learning_rate = 0.01
# 二元交叉熵损失
criterion = nn.BCELoss()
# SGD 优化器
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
criterion, optimizer
#输出
'''(BCELoss(),
SGD (
Parameter Group 0
dampening: 0
lr: 0.01
momentum: 0
nesterov: False
weight_decay: 0
))'''
模型的训练
for epoch in range(num_epochs):
y_pred = model(X_train)
loss = criterion(y_pred, y_train)
# 后向传播、梯度更新、梯度清空
loss.backward()
optimizer.step()
optimizer.zero_grad()
if (epoch+1) % 10 == 0:
print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
print("模型训练完毕!!")
#输出
"""
epoch: 10, loss = 0.4921
epoch: 20, loss = 0.4176
epoch: 30, loss = 0.3683
epoch: 40, loss = 0.3331
epoch: 50, loss = 0.3066
epoch: 60, loss = 0.2858
epoch: 70, loss = 0.2689
epoch: 80, loss = 0.2548
epoch: 90, loss = 0.2428
epoch: 100, loss = 0.2325
模型训练完毕!!
"""
综上,我们训练好了一个乳腺癌的预测模型。我们可以尝试对任意一条数据进行预测:
index = np.random.randint(0, len(X_test))
y_predicted = model(X_test[index])
# 小于 0.5 则输出 0 ,大于0.5 则输出 1
y_predicted_cls = y_predicted.round()
# 将结果转为 numpy类型
real = y_test[index].detach().numpy()[0]
predict = y_predicted_cls.detach().numpy()[0]
print("第 {} 条测试数据的真实结果为 {} ,预测结果为 {} "
.format(index, real, predict))
#第 74 条测试数据的真实结果为 1.0 ,预测结果为 1.0
那么,我们训练出来的模型准确率到底是多少呢?
with torch.no_grad():
y_predicted = model(X_test)
y_predicted_cls = y_predicted.round()
acc = y_predicted_cls.eq(y_test).sum().numpy() / float(y_test.shape[0])
print(f'accuracy: {acc.item():.4f}')
#accuracy: 0.9035
总结:
其实,本实验建立的一个线性函数+激活函数的模型就是一个简单的神经网络模型。全连接神经网络的实质其实就是无数个线性函数和非线性网络组成的集合。