Python:实现 PyTorch 中训练自定义卷积神经网络模型(CNN)并导出模型为 ONNX 格式,同时使用 ONNX Runtime 进行推理

本文将介绍如何使用 PyTorch 训练一个简单的卷积神经网络(CNN)模型,并将训练好的模型导出为 ONNX 格式,之后使用 ONNX Runtime 在 Python 中加载并进行推理。

在开始使用模型前,需要安装以下 Python 库。为了提高下载速度,我们将使用国内镜像源进行安装。以下是各个库的安装步骤。

1. 安装 onnxruntime

onnxruntime 是用于加载和运行 ONNX 模型的库。可以通过以下命令使用国内镜像源进行安装:

pip install onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple

 

2. 安装 pandas

pandas 是用于数据处理和分析的库,特别适合处理表格数据。通过以下命令安装:

pip install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

 

3. 安装 PillowPIL

Pillow 是 Python 的图像处理库,PIL 是它的别名。用于图像读取和处理。安装命令如下:

pip install Pillow -i https://pypi.tuna.tsinghua.edu.cn/simple

 

4. 安装 scikit-learn

scikit-learn 是常用的机器学习库,包含数据预处理、模型训练等功能。使用以下命令安装:

pip install scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple

1. 数据集和数据加载

我们将使用一个简单的手写数字分类数据集——Pendigits,这是一个包含手写数字的 CSV 文件。首先,我们需要处理数据并创建一个 PyTorch 数据集。

自定义数据集类:
import torch
from torch.utils.data import Dataset, DataLoader
import pandas as pd
from sklearn.preprocessing import StandardScaler

class PendigitsDataset(Dataset):
    def __init__(self, csv_file):
        self.data = pd.read_csv(csv_file, header=0)
        self.features = self.data.iloc[:, 1:-1].values
        self.labels = self.data.iloc[:, -1].values

        # 数据标准化
        self.scaler = StandardScaler()
        self.features = self.scaler.fit_transform(self.features)
    
    def __len__(self):
        return len(self.features)

    def __getitem__(self, idx):
        sample = torch.tensor(self.features[idx], dtype=torch.float32)
        label = torch.tensor(self.labels[idx], dtype=torch.long)
        sample = sample.view(1, 28, 28)  # 转换为28x28图像格式
        return sample, label

PendigitsDataset 类中,__init__ 方法加载 CSV 文件并进行数据标准化;__getitem__ 方法将每个样本转换为一个 28x28 的图像格式。

数据加载器:
trainset = PendigitsDataset(csv_file='path_to_csv_file')
trainloader = DataLoader(trainset, batch_size=32, shuffle=True)
2. 定义卷积神经网络模型(CNN)

我们使用两个卷积层和两个全连接层来构建一个简单的 CNN。

import torch.nn as nn

class CNN(nn.Module):
    def __init__(self, output_size=10):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(64 * 7 * 7, 128)
        self.fc2 = nn.Linear(128, output_size)
        self.relu = nn.ReLU()
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        x = self.relu(self.conv1(x))
        x = self.pool(x)
        x = self.relu(self.conv2(x))
        x = self.pool(x)
        x = x.view(-1, 64 * 7 * 7)  # 展平
        x = self.relu(self.fc1(x))
        x = self.fc2(x)
        return self.softmax(x)
3. 训练模型

接下来,我们将训练模型并使用优化器和损失函数进行训练。

import torch.optim as optim

# 初始化模型,损失函数,优化器
model = CNN(output_size=10)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
epochs = 100
for epoch in range(epochs):
    running_loss = 0.0
    for inputs, labels in trainloader:
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
    
    print(f"Epoch {epoch+1}, Loss: {running_loss/len(trainloader)}")
4. 导出模型为 ONNX 格式

完成训练后,我们可以将模型导出为 ONNX 格式,以便在其他平台(如 ONNX Runtime)中进行推理。

import torch.onnx

# 导出模型为ONNX格式
onnx_path = 'cnn_model.onnx'
dummy_input = torch.randn(1, 1, 28, 28).to(device)  # 创建一个与训练数据形状相同的虚拟输入
torch.onnx.export(model, dummy_input, onnx_path, input_names=['input'], output_names=['output'])

print(f"模型已导出为 ONNX 格式,路径为 {onnx_path}")

完整代码

import torch
import torch.onnx
import torch.nn as nn
import torch.optim as optim
import pandas as pd
from torch.utils.data import Dataset, DataLoader
from sklearn.preprocessing import StandardScaler

# 自定义数据集类
class PendigitsDataset(Dataset):
    def __init__(self, csv_file, transform=None):
        # 读取CSV文件,并跳过第一行(列名)
        self.data = pd.read_csv(csv_file, header=0)  # header=0 表示第一行是列名
        self.transform = transform
        
        # 去除不需要训练的列(如 a1, q1)
        # 假设这些列在前几列,并且标签列是最后一列
        # 获取特征数据(所有列,去掉最后一列标签)
        self.features = self.data.iloc[:, 1:-1].values  # 去掉第一列和最后一列(即标签)
        self.labels = self.data.iloc[:, -1].values  # 标签列
        
        # 数据标准化(如果需要)
        self.scaler = StandardScaler()
        self.features = self.scaler.fit_transform(self.features)  # 标准化特征数据
    
    def __len__(self):
        return len(self.features)

    def __getitem__(self, idx):
        sample = torch.tensor(self.features[idx], dtype=torch.float32)
        label = torch.tensor(self.labels[idx], dtype=torch.long)
        
        if self.transform:
            sample = self.transform(sample)
        
        return sample, label

# 定义MLP模型
class MLP(nn.Module):
    def __init__(self, input_size=16, hidden_size=128, output_size=10):
        super(MLP, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, hidden_size)
        self.fc3 = nn.Linear(hidden_size, output_size)
        self.relu = nn.ReLU()
        self.softmax = nn.Softmax(dim=1)

    def forward(self, x):
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)
        return self.softmax(x)

# 数据预处理
transform = None  # 你可以根据需要定义transform

# 使用Pendigits数据集
trainset = PendigitsDataset(csv_file=r'pendigits_txt.csv', transform=transform)# 你的csv格式数据集路径
trainloader = DataLoader(trainset, batch_size=32, shuffle=True)  # 增加batch_size

# 初始化模型,损失函数,优化器
model = MLP(input_size=trainset.features.shape[1])  # 输入大小是特征的数量

# 使用GPU训练(如果可用)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 加载模型(如果有保存的模型)
checkpoint_path = 'mlp_model_checkpoint.pth'
start_epoch = 0
try:
    checkpoint = torch.load(checkpoint_path)
    model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
    start_epoch = checkpoint['epoch'] + 1  # 从保存的epoch继续
    print(f"模型从epoch {start_epoch}开始恢复训练")
except FileNotFoundError:
    print("没有找到之前的模型,开始新训练")

# 训练模型
epochs = 100
for epoch in range(start_epoch, start_epoch + epochs):
    running_loss = 0.0
    for inputs, labels in trainloader:
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
    print(f"Epoch {epoch+1}, Loss: {running_loss/len(trainloader)}")

    # 每训练一定的epoch保存一次模型
    if (epoch + 1) % 20 == 0:  # 每20个epoch保存一次
        checkpoint_filename = f'mlp_model_checkpoint_{epoch-19}-{epoch}.pth'
        torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
        }, checkpoint_filename)
        print(f"模型已保存至 {checkpoint_filename}")

# 导出ONNX模型
dummy_input = torch.randn(1, trainset.features.shape[1]).to(device)  # 确保dummy_input也在GPU上
onnx_path = "mlp_model.onnx"
torch.onnx.export(model, dummy_input, onnx_path, input_names=['input'], output_names=['output'])

print(f"模型已导出到 {onnx_path}")

 

5. 使用 ONNX Runtime 进行推理

我们可以使用 onnxruntime 来加载训练好的 ONNX 模型,并使用它进行推理。

import onnx
import onnxruntime as ort
import numpy as np
from PIL import Image
from torchvision import transforms

# 加载训练好的 ONNX 模型
model_path = "cnn_model.onnx"
session = ort.InferenceSession(model_path)

# 预处理函数:将图片调整为 28x28 尺寸并转为灰度图像、张量
transform = transforms.Compose([
    transforms.Grayscale(num_output_channels=1),  # 转为灰度图
    transforms.Resize((28, 28)),  # 调整为 28x28 尺寸
    transforms.ToTensor(),  # 转为张量
    transforms.Normalize((0.5,), (0.5,))  # 归一化
])

# 加载并预处理输入图片
image_path = "path_to_image.jpg"  # 替换为你的图片路径
image = Image.open(image_path)
image = transform(image).numpy()  # 转为 NumPy 数组

# 进行推理(预测)
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
outputs = session.run([output_name], {input_name: image.astype(np.float32)})

# 获取预测结果
predicted_class = np.argmax(outputs[0], axis=1)
print(f"预测的类是: {predicted_class[0]}")
6. 总结

本文介绍了如何在 PyTorch 中训练一个 CNN 模型,并将其导出为 ONNX 格式。我们还展示了如何使用 ONNX Runtime 在 Python 中加载导出的模型并进行推理。通过这种方式,你可以轻松地将 PyTorch 模型部署到不同的平台并进行推理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值