乳腺癌分类模型的定义中,必须有_init_(初始化)函数和forward(正向传播)函数
乳腺癌分类模型定义
# 自定义模型
class MyModel(torch.nn.Module):
def __init__(self,in_features):
super(MyModel,self).__init__() #调用父类的构造函数!
# 搭建自己的神经网络
# 1.构建线性层
#该层接受具有in_features数量的输入特征,并产生一个具有单个输出特征的结果
self.linear = torch.nn.Linear(in_features,1)
# 2.构建激活函数层
self.sigmoid = torch.nn.Sigmoid()
def forward(self,x):
"""重写了父类的 forward 函数,正向传播"""
pred = self.linear(x)
out = self.sigmoid(pred)
return out
神经网络定义完成后:
①确定损失函数,学习率
②构建神经网络模型对象
③构建优化器对象,并为优化器指定模型参数和学习率
# 二分类的交叉熵损失公式
loss = troch.nn.BCELoss()
# 学习率、迭代次数
learning_rate = 0.1
num_epochs = 100
# 获取样本量和特征数,创建模型
n_samples,n_features = X.shape
model = MyModle(n_features)
# 创建优化器
optimizer = torch.optim.SGD(model.parameters(),lr=learning_rate)
# 打印模型、打印模型参数
print(model)
print(list(model.parameters()))
模型参数集合中包括了两个子集,分别对应线性层self.linear当中的30个w和1个b
训练模型的参数
threshold_value = 0.00001
for epoch in range(num_epochs):
y_pred = model(X_train) # 正向传播,调用 forward()方法
ls = loss(y_pred,Y_train) # 计算损失(标量值)
ls.backward() # 反向传播
optimizer.step() # 更新权重
optimizer.zero_grad() # 清空梯度
if epoch%5 == 0:
print(f"epoch:{epoch},loss={ls.item():.4f}")
if ls.item() <= threshold_value:
break;
print("模型训练完成! loss={}".format(ls))
计算模型预测的准确率
with torch.no_grad(): # 无需向后传播(非训练过程)
y_pred = model(X_test)
# 上面计算出来的结果是 0-1 之间的数,将数据进行四舍五入,得到 0 或 1
y_pred_cls = y_pred.round()
# 统计结果
# 如果相同,则为True,返回值为1;否则返回值为0,sum则是对该值相加
acc = y_pred_cls.eq(Y_test).sum().numpy()
/ float(Y_test.shape[0])
print(f"准确率:{acc.item():.4f}")
模型训练的数据分批
由于计算本身以及显存容量的限制,不可能一次性将所有数据全部加载到内存中进行计算,需要将训练集分批,把训练集合中的数据随机分成等量的几份,按批次迭代训练。
pytorch提供了torch.utils.data.DataLoader加载器,该加载器可以自动地将输入的数据进行打乱和分批。
DataLoader()的加载参数
dataset: 需要打乱的数据集
batch_size: 每一批数据条数量
shuffle: True或者False,表示是否将数据打乱后再分批
如果要使用pytorch中的DataLoader,则需要
①创建一个继承dataset类的子类对象
因为DataLoader只能处理继承了Dataset类的子类对象
②创建一个dataloader对象
③迭代dataloader对象,通过其将data,label批量传入模型
数据的归一化处理
transform=torchvision.transforms.ToTensor()
是PyTorch和torchvision库中的一个常见语法,用于图像预处理。具体来说,torchvision.transforms.ToTensor()
是一个函数,它接受一个PIL图像或NumPy ndarray,并返回一个PyTorch张量(Tensor)。这个函数的主要作用是将图像的像素值从0-255的整数范围转换为0-1的浮点数范围。
import numpy as np
# 用于绘图的库
import matplotlib.pyplot as plt
features,label = train_dataset[0]
print("第一个数据对应的标签名称:",label)
print(features.shape,label)
# 原始数据是归一化后的数据,因此这里需要反归一化
img = np.array(features)*255
img = img.reshape(28,28)
# 使用matplotlib的imshow函数来显示灰度图像
plt.imshow(img,"gray")
plt.show()
多分类问题
Softmax函数
在多分类问题中,我们需要分类器输出每种分类的概率,并且概率之和为1.因此,将模型对不同分类的预测值映射为0-1之间的实数,并且通过归一化保证和为1.
神经网络用于多分类问题时,会将输出的最后一层,加上Softmax函数,用于数据的归一化输出
import numpy as np
def softmax(x):
return np.exp(x)/np.sum(np.exp(x))
x = np.array([2.0,1.0,0.1])
outputs = softmax(x)
print(x)
print(outputs)
print(outputs.sum())
损失函数
代价函数也称为损失函数,反映的是预测结果和实际结果之间的差距,衡量当前预测结果的可用性和准确性。交叉熵是信息论领域的一种度量方法,它建立在熵的基础上,通常计算两种概率分布之间的差异。交叉熵是用来描述两个分布的距离的,神经网络训练的目的就是使预测结果的分布逼近真实的分布。
二分类交叉熵
在二分类的情况下,模型最终预测的结果概率为p和1-p,此时二分类交叉熵为
其中,y表示样本标签,正样本标签为1,负样本标签为0;p表示预测为正样本的概率
通过 torch.nn.BCELoss
计算二分类交叉熵
from torch import nn
loss = nn.BCELoss()
# 假设两个模型最后的预测结果相同,但是概率不同
model_one_pred = torch.tensor([0.9, 0.1])
model_two_pred = torch.tensor([0.6, 0.4])
#真实结果
target = torch.FloatTensor([1, 0])
#计算两种模型的损失
l1 = loss(model_one_pred, target)
l2 = loss(model_two_pred, target)
print(l1, l2)
多分类交叉熵
其中,K是种类数量,y是标签(如果类别是i,则等于1;否则等于0);p是神经网络的输出,也就是类别是i的概率,这个输出值由softmax计算得来的值
pytorch中,通过torch.nn.CrossEntropyLoss计算多分类交叉熵
loss = torch.nn.CrossEntropyLoss()
Y = torch.tensor([2,1,0])
model1_pred = torch.tensor(
[[0.3, 0.3, 0.4], # predict class 2, right(weak)
[0.3, 0.4, 0.3], # predict class 1, right(weak)
[0.1, 0.2, 0.7]] # preiict class 2, Wrong(badly)
)
model2_pred = torch.tensor(
[[0.1, 0.2, 0.7], # predict class 2, right(strong)
[0.1, 0.7, 0.2], # predict class 1, right(strong)
[0.4, 0.3, 0.3]]) # predict class 0, right(weak)
ls1 = loss(model1_pred,Y)
ls2 = loss(model2_pred,Y)
print(ls1, ls2)
激活函数
Sigmoid函数
import numpy as np
# Matplotlib是Python中用于绘制图表和图形的库
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# -10到10之间取100个数
x = np.linspace(-10, 10 ,100)
plt.plot(x, sigmoid(x), 'b')
# 在图上添加网格线,并设置网格线的样式为虚线
plt.grid(linestyle='--')
plt.show()
Tanh函数
def tanh(x):
return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
x = np.linspace(-10, 10 ,100)
plt.plot(x, tanh(x), 'b')
ReLU激活函数
import numpy as np
import matplotlib.pyplot as plt
import torch
def relu(x):
return np.where(x >= 0, x, 0)
x = np.linspace(-10, 10, 1000)
y = relu(x)
plt.plot(x, y, 'b')
plt.grid(linestyle='--')
plt.show()
神经网络是由多个神经元(线性函数)组成,从宽度上拓展神经元的数量,从深度上拓展而增加多层时,如果没有激活函数,整个神经网络仍然是一个线性模型(可推导),也就无法解决非线性问题。
多层神经网络的建立
在利用 PyTorch 建立神经网络模型时,需要注意以下几点:
-
自定义的神经网络模型必须继承 nn.Module
-
自定义的类中需要实现:
-
init: 定义网络的结构
-
forward: 定义数据在模型中的正向传播路径
-
import torch
# 神经网络的建立
class NeuralNet(torch.nn.Module):
def __init__(self,input_size,hidden_size,out_size):
super(NeuralNet, self).__init__()
self.linear1 = torch.nn.Linear(
input_size,hidden_size
)
self.relu = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(hidden_size,out_size)
# 二分类使用Sigmoid()函数,多分类使用Softmax()函数
self.relu = torch.nn.Sigmoid()
def forward(self,x):
out = self.linear1(x)
out = self.relu(out)
out = self.linear2(out)
out = self.sigmoid(out)
return out
hidden_size通常要比input_size大一倍
保存数据和训练数据
train_test_ptFile = './pt/train_test_pt_for_MNIST.pt'
torch.save((train_dataset, test_dataset),train_test_ptFile)
使用PyTorch库将两个数据集(
train_dataset
和test_dataset
)保存到一个单一的.pt
文件中。这是一种常见的做法,用于保存训练数据和测试数据,以便在之后可以轻松地加载它们,而无需重新加载或重新处理原始数据
import torch
train_test_ptFile = './pt/train_test_pt_for_MNIST.pt'
train_dataset, test_dataset = torch.load(train_test_ptFile)
创建数据加载器
from torch.utils.data import DataLoader
train_dataLoader = DataLoader(
dataset=train_dataset,
# 每次迭代过程中,模型接收100个样本进行训练
batch_size=100,
shuffle=True
)
test_dataLoader = DataLoader(
dataset=test_dataset,
batch_size=100,
shuffle=True
)
创建神经网络模型
class HWR_Model(torch.nn.Module):
def __init__(self,in_features, hidden_size, out_classes):
super(HWR_Model,self).__init__()
self.linear1 = torch.nn.Linear(in_features, hidden_size)
self.relu = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(hidden_size, out_classes)
self.Softmax = torch.nn.Softmax(dim=1)
def forward(self,x):
out = self.linear1(x)
out = self.relu(out)
out = self.linear2(out)
out = self.Softmax(out)
return out
model = HWR_Model(28*28, 500, 10).to(device)
lossF = torch.nn.CrossEntropyLoss()
# 自适应学习率,适合处理大规模数据和参数
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
神经网络模型训练
在神经网络训练过程中,我们通常使用梯度下降或其变种(如Adam、RMSprop等)来更新网络的权重。这些更新是基于损失函数(或目标函数)对权重的梯度。
具体来说,训练过程通常涉及以下步骤:
-
前向传播:输入数据通过网络生成输出。
-
计算损失:根据网络的输出和实际标签计算损失。
-
反向传播:计算损失对网络中每个权重的梯度。
-
权重更新:使用优化器(optimizer)和计算出的梯度来更新权重。
for epoch in range(5):
# enumerate函数会返回每个批次的索引(i)和该批次的数据(features, labels)
for i,(features, labels) in enumerate(train_dataLoader):
# view 方法用于改变一个张量(tensor)的形状(shape)而不改变其数据
features = features.view(-1, 28*28).to(device)
labels = labels.to(device)
pred = model(features)
ls = lossF(pred, labels)
ls.backward()
# 使用在之前的反向传播步骤中计算出的梯度来更新网络的权重并清除这些梯度,以便下一次迭代
optimizer.step()
optimizer.zero_grad()
if i % 100 ==0:
print(f'loss={ls:.8f}')
break
break
迭代访问测试数据集
# 通过迭代来逐个访问数据集中的数据
sample = iter(test_dataLoader)
# next()函数会返回批次中的数据和标签
# 这段代码的作用是从test_dataLoader中获取一个批次的样本数据和标签,并将它们分别存储在features和labels变量
features, labels = next(sample)
创建图像
import matplotlib.pyplot as plt
for i in range(5):
# 在一个图形窗口中创建一个 1 行 5 列的子图布局,并选择第 i+1 个子图进行操作(即绘制数据或图像)
# 在一个窗口中绘制多个子图,每个子图显示不同的数据或图像
plt.subplot(1, 5 , i+1)
# 将 features 数组中的第 i 个样本的第 0 个通道的图像数据以灰度模式显示出来
# 在一个窗口中绘制多个子图,每个子图显示不同的数据或图像
plt.imshow(features[i][0], cmap='gray')
# 接下来的代码无需计算梯度
with torch.no_grad():
pred = model(features)
print(pred[:5].int())
# 在一个指定的轴上找到张量的最大值。axis=1意味着我们在每一行(即,对于每一个样本)上寻找最大值。对于二维张量,axis=0会沿着列方向(垂直方向)寻找最大值,
# 而axis=1会沿着行方向(水平方向)寻找最大值
# 只获取第二个值,即最大值的索引;第一个为占位符,应该返回的是最大值
_ , indeces = torch.max(pred[:5], axis=1)
print('预测值为:',indeces)
print('真实值为:',labels[:5])
模型准确率
with torch.no_grad():
num_corrects = 0
num_samples = 0
for features, labels in test_dataLoader:
features = features.view(-1, 28*28)
pred = model(features)
_, indeces = torch.max(pred, axis=1)
num_corrects += (indeces == labels).sum().item()
num_samples += len(labels)
# break
print(f'模型的准确率是:{num_corrects/num_samples:.2%}')
保存并加载模型
# 保存模型
model_state_ptFile = './model/hand_writing_model_states.pt'
torch.save(model.state_dict(), model_state_ptFile)
# 读取和装载模型参数
state_dict = torch.load(model_state_ptFile)
model = HWR_Model(28 * 28, hidden_size, 10)
model.load_state_dict(state_dict=state_dict)
CSV文件
*.CSV(Comma-Separated Values)文件由任意数目的记录组成,每条记录由字段组成,字段间以‘逗号’或‘制表符’分隔。该文件以纯文本形式存在,通常用于存储表格数据(数字和文本)
import pandas as pd