[pytorch实战]基于pytorch、keras、pyqt5搭建神经网络,完成基于多层感知机MLP、SVM、Transformer的辅助诊断gui程序

目录

  • 本文章主要展示模型的代码,具体解释请看程序中的注释,编写的函数都可以比较方便的调用
  • 程序展示
    • 界面
    • 各文件作用
  • 模型搭建
    • MLP多层感知机
    • Transformer
    • 训练模型
    • 预测
  • 所有项目文件已上传资源,可自行下载

本文章主要展示模型的代码,具体解释请看程序中的注释,编写的函数都可以比较方便的调用

程序展示

界面

原始界面如下图所示,输入患者的各项数据,选择使用的模型,点击预测可以在右侧输出框中输出分类结果。
[pytorch实战]基于pytorch、keras、pyqt5搭建神经网络,完成基于多层感知机MLP、SVM、Transformer的辅助诊断gui程序_第1张图片
点击模型页可以配置三个分类器的参数。可调节训练集的划分比例,随机种子可以控制每次划分是否相同。预测内容中包括死亡和几类疾病。
点击训练,即可训练该模型;点击测试即可输出该模型的测试准确率。
[pytorch实战]基于pytorch、keras、pyqt5搭建神经网络,完成基于多层感知机MLP、SVM、Transformer的辅助诊断gui程序_第2张图片

各文件作用

model用来存放训练好的模型
allmodel.py 搭建神经网络模型
train.py 训练模型
test_model.py 测试模型
data_processing.py 数据预处理
main.py 测试各函数是否正常运行
gui.py 直接运行此程序即可打开上述界面
data文件整理自MIMIC-III数据集
[pytorch实战]基于pytorch、keras、pyqt5搭建神经网络,完成基于多层感知机MLP、SVM、Transformer的辅助诊断gui程序_第3张图片

下面,我将一步一步描述程序的完成过程

模型搭建

SVM是用Keras实现的,实现非常简单不做赘述。
其余两个模型使用pytorch实现

MLP多层感知机

最简单的MLP结构

class MLP(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.dropout = nn.Dropout(p=0.5)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, num_classes)   
    def forward(self, x):
        out = self.fc1(x)
        out = self.dropout(out)
        out = self.relu(out)
        out = self.fc2(out)
        return out

当隐藏层为500时结构如下:
[pytorch实战]基于pytorch、keras、pyqt5搭建神经网络,完成基于多层感知机MLP、SVM、Transformer的辅助诊断gui程序_第4张图片

Transformer

该部分仅使用了transformer的encoder部分,从ViT的代码上改的。完整ViT的实现可以参考这篇博客
ViT的结构如下,因为我的输入是各项实验室指标,是19个一维的特征,因此我将图像部分移除了,直接输入 x(batch,feature_n,dim) (一批数据的个数,特征个数,特征维度),本程序中,feature_n即为19,维度dim为1.这个参数都不需要特别输入,只需将输入样本调整为相应格式即可。
[pytorch实战]基于pytorch、keras、pyqt5搭建神经网络,完成基于多层感知机MLP、SVM、Transformer的辅助诊断gui程序_第5张图片

class PreNorm(nn.Module):
    def __init__(self, dim, fn):
        super().__init__()
        self.norm = nn.LayerNorm(dim)
        self.fn = fn
    def forward(self, x, **kwargs):
        return self.fn(self.norm(x), **kwargs)

class FeedForward(nn.Module):
    def __init__(self, dim, hidden_dim, dropout = 0.):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(dim, hidden_dim),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(hidden_dim, dim),
            nn.Dropout(dropout)
        )
    def forward(self, x):
        return self.net(x)

class Attention(nn.Module):
    def __init__(self, dim, heads , dim_head , dropout ):
        super().__init__()
        inner_dim = dim_head *  heads
        project_out = not (heads == 1 and dim_head == dim)

        self.heads = heads
        self.scale = dim_head ** -0.5

        self.attend = nn.Softmax(dim = -1)
        self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)

        self.to_out = nn.Sequential(
            nn.Linear(inner_dim, dim),
            nn.Dropout(dropout)
        ) if project_out else nn.Identity()

    def forward(self, x):
        qkv = self.to_qkv(x).chunk(3, dim = -1)
        q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = self.heads), qkv)

        dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale

        attn = self.attend(dots)

        out = torch.matmul(attn, v)
        out = rearrange(out, 'b h n d -> b n (h d)')
        return self.to_out(out)

class Transformer(nn.Module):
    def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout = 0.):
        super().__init__()
        self.layers = nn.ModuleList([])
        for _ in range(depth):
            self.layers.append(nn.ModuleList([
                PreNorm(dim, Attention(dim, heads = heads, dim_head = dim_head, dropout = dropout)),
                PreNorm(dim, FeedForward(dim, mlp_dim, dropout = dropout))
            ]))
    def forward(self, x):
        for attn, ff in self.layers:
            x = attn(x) + x
            x = ff(x) + x
        return x

class ViT(nn.Module):
'''             
             num_classes=2,   #分类数量
             dim = 1     ,   #每个特征的维度
             depth= 1,          #encoder层数
             heads=1,       #注意力头数
             mlp_dim=32,    #encoder 中 MLP 输出
             pool = 'cls',  #分类方式 cls or mean
             dim_head = 32,  #注意力输出向量维度
             dropout = 0.,     #transformer里的dropout
             emb_dropout = 0.   #emb的dropout
'''

    def __init__(self,  num_classes, dim, depth, heads, mlp_dim, pool, dim_head, dropout = 0., emb_dropout=0. ):
        super().__init__()


        assert pool in {'cls', 'mean'}, 'pool type must be either cls (cls token) or mean (mean pooling)'

        self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
        self.dropout = nn.Dropout(emb_dropout)

        self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)  #dim:输入每个特征维度,dim_head,q和k维度

        self.pool = pool
        self.to_latent = nn.Identity()

        self.mlp_head = nn.Sequential(
            nn.LayerNorm(dim),
            nn.Linear(dim, num_classes)
        )

    def forward(self, x):       #b为批大小,x(batch,feature_n,dim)
        b,_ =x.shape
        cls_tokens = repeat(self.cls_token, '() n d -> b n d', b = b)   
        x = torch.unsqueeze(x, 2)   #输入特征是1维的,所以要增添这一维度,否则不用
        x = torch.cat((cls_tokens, x), dim=1)   #(1, 65, 1024)  65:加cls之后的feature数,1024是每个特征的维度
        x = self.dropout(x)

        x = self.transformer(x)

        x = x.mean(dim = 1) if self.pool == 'mean' else x[:, 0]

        x = self.to_latent(x)
        return self.mlp_head(x)

训练模型

以MLP为例,编写了函数,为了更方便地与前端gui进行对接,可对模型和训练的超参数进行修改。最后会保存模型,方便预测时进行调用。

def train_MLP(train_data,    #训练数据
             train_label,   #训练标签
             hidden_size,   #隐藏层大小
             num_classes,   #分类数量
             num_epochs,    #训练轮数
             batch_size,    #批大小
             learning_rate  #学习率
             ):
    input_size = train_data.shape[1] 
    torch_dataset = Data.TensorDataset(train_data, train_label)
    loader = Data.DataLoader(          #批训练数据加载器
        dataset=torch_dataset,
        batch_size=batch_size,
        shuffle=True,  # 每次训练打乱数据, 默认为False
        num_workers=0,  # 使用多进行程读取数据, 默认0,为不使用多进程
        drop_last=False
    )
    model = allmodel.MLP(input_size, hidden_size, num_classes)
    print(model)
    summary(model,(1,input_size))
    model.train()
    # Loss and optimizer
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
    # 训练模型
    total_step = math.ceil(len(train_data)/batch_size)
    for epoch in range(num_epochs):
        for i, (input_data, labels) in enumerate(loader):
            # 前向传播
            outputs = model(input_data)
            loss = criterion(outputs, labels.long())
            # 
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            # 每一百步打印一次
            if (i + 1) % 10 == 0:
                print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'
                      .format(epoch + 1, num_epochs, i + 1, total_step, loss.item()))
    print("MLP训练完毕")
    # 保存训练完的模型
    torch.save(model, 'model/MLP.pth')

预测

此部分主要注意输入的size要符合模型输入的条件。

def predict(x,model_name):
    x=x.reshape(1,len(x))
    if model_name == 'SVM' :
        model = joblib.load("model//SVM.pth")
        predicted = model.predict(x)
    else :   
        input_data = torch.tensor(x).to(torch.float32) #将array转变为tensor
        model = torch.load('model//%s.pth'%model_name)
        outputs = model(input_data)
        _, predicted = torch.max(outputs.data, 1)
        predicted = predicted.item()
    if predicted == 1:
        out_pre = '是'
    elif predicted == 0:
        out_pre = '否'
    print (out_pre)
    return out_pre

最后,使用PyQt5编写界面。

所有项目文件已上传资源,可自行下载

你可能感兴趣的:(python机器学习,pytorch,神经网络,keras,python,pyqt5)