鸢尾花(iris)分类——基于pytorch+python实现

鸢尾花(iris)分类——基于pytorch+python实现

简介

鸢尾花分类是一个简单的入门项目,我将该项目分成了三个项目来实现:

  1. 基础版,只对3个鸢尾花类别进行分类
  2. 基础版2,加上一个类别,用于区分不属于这三类鸢尾花
  3. 基础版3,实现一个GUI

下面会给出代码以及数据集的链接,并简单分析部分代码,以及本人遇到的一些问题,还有一些思考

资源链接

数据集介绍

数据集在iris文件夹中的iris.data,数据集的介绍在iris.name,数据集的前4列分别为 sepal length,sepal width, petal length, petal width。第5列为鸢尾花的类别。

下面代码中读取数据集,使用sklearn的load_iris(),而不是直接读取iris.data文件夹

部分代码分析

下面的代码分析是对于基础版

1. 对数据进行打乱

load_iris()返回的是一个字典。

由于原数据对3个类别是有顺序的,因而需要先将数据打乱,设计随机数种子,保证下次运行可以得到相同结果。

# 读取数据集
iris = datasets.load_iris()
data = iris.data
target = iris.target

# 对数据打乱
np.random.seed(116)
indices = np.arange(data.shape[0])
np.random.shuffle(indices)
data = data[indices]
target = target[indices]
2. 利用TensorDataset()进行数据加载
# 加载数据集
dataset_train = TensorDataset(train_data, train_target)
dataloader_train = DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True)
dataset_test = TensorDataset(test_data, test_target)
dataloader_test = DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=True)
3. 网络结构

这个网络及其简单,只用了2层线性结构,但是准确率已经很高了,我也不知道网络结构是怎么设计的,有没有什么方法,你可以修改这个网络,看看效果。

# 创建感知器模型
class irisClassify(nn.Module): 
    def __init__(self): 
        super().__init__()
        self.module = Sequential(
            Linear(4, 10), 
            ReLU(), 
            Linear(10, 3), 
            Softmax(dim=1)
        )
    def forward(self, data): 
        output = self.module(data)
        return output
4. 网络保存

训练过程就是常规的按照pytorch方法进行即可。

由于在基础班3中需要使用模型对输入进行预测,因而需要对模型进行保存。GUI实现的效果为:

在这里插入图片描述

保存思路:从上一个项目得出经验,最后一次训练出来的结果未被损失最小,因而我对每轮训练都进行了验证,保存了每次准确率>=上一次以及最后一次的训练结果。

但是由于本次数据集较小,训练速度快,同时准确率较高,所以这么做也不太有必要。

注意: 按照下面的代码,你需要创建用于保存的文件夹,并替换下面的路径

 with torch.no_grad():
        total_right = 0
        for data in dataloader_test: 
            testData, testTraget = data    # (6, 4), (6,)
            output = iris_recognition_module(testData)
            right = (output.argmax(1) == testTraget).sum()
            total_right += right
        print(f"第{epoch}次训练的准确率为:  {1.0 * total_right / len(test_data):.3f}")
        if total_right >= bestRightNum or epoch == EPOCH - 1:
            bestRightNum = total_right
            path = f"./4_models/iris_reiris_recognition_model_{epoch}.pth"
            torch.save(iris_recognition_module.state_dict(), path)
5. 基础版2的第4个类训练

不妨称第4个类为反类。

我的思路是捏造一些数据,用来训练第4个类别。记得修改网络最后的Linear()。

关于训练第四个类遇到的问题在下面会提及。

# 反类训练数据加入
torch.manual_seed(42)    # 保证运行产生随机数相同
other_data = torch.randint(low=20, high=100, size=(100, 4)).float()
other_train_data = other_data[0:80]
other_test_data = other_data[80:]
train_data = torch.vstack((train_data, other_train_data))
test_data = torch.vstack((test_data, other_test_data))
train_target = torch.cat((train_target, (3 * torch.ones(80)).long()))
test_target = torch.cat((test_target, (3 * torch.ones(20)).long()))
6. 其他

对于GUI部分的代码是由gpt编写,不解释。

遇到的问题

1. 数据类型问题

pytorch中Linear()等默认数据类型为torch.float32,标签默认的数据类型为toch.long,而使用sklearn中的load_iris读取出来的数据类型为np.float64,故需要更改数据类型。标签读取出来的数据类型为np.int,也需要更改数据类型。

2. 在基础版2中训练次数与捏造数据问题

一开始我将捏造的数据的4个范围设置在20-1000,因而为了覆盖这个范围,生成的数据要远高于原来训练数据个数120,导致准确率极低,几乎全被分类为第4类,所以把数据范围放小到20-100,这样覆盖该范围需要的数据个数较少80个,同时为了保证效果,增加了训练轮数。

思考

1. 神经网络的建构是怎么得到的?

比如在本次项目中,只是简单的使用了一个两层的神经网络(随手写的),准确率还不错,那么对于哪些比较复杂的网络是怎么得到的呢?有没有严格的推导或者方法?如果有了解的,可以在评论区分享一下论文或者blog。

2. 训练反类这种问题?

本次项目中通过捏造数据的方法进行训练,这也只是一种直观感觉,这类问题有没有相关的研究,叫什么? 可以在评论区分享一下论文或者blog。

3. GUI编写?

GUI的编写完全是由gpt3.5完成的,只在它的基础上进行了修改。对于这种非核心部分貌似没必要花时间掌握。

代码

由于代码并不长,因而在后面贴上源码,对于使用到的图片,可以由上面链接下载,或者自己随便弄几张即可。

1. 神经网络训练模型.ipynb
import pandas as pd 
import sklearn
import torch
import torch.nn as nn
import numpy as np
from sklearn import datasets
from torchvision import transforms
from torch.nn import Linear, Sequential, ReLU, Softmax
from torch.utils.data import TensorDataset, DataLoader\


from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity='all'


EPOCH = 80
BATCH_SIZE = 6
LR = 0.02


# 读取数据集
iris = datasets.load_iris()
data = iris.data
target = iris.target

# 对数据打乱
np.random.seed(116)
indices = np.arange(data.shape[0])
np.random.shuffle(indices)
data = data[indices]
target = target[indices]

# 转换为tensor, .float()转换为float32,因为Linear()默认为float32
data = torch.from_numpy(data).float()         # 150 * 4
# target需要保证为long类型
target = torch.from_numpy(target).long()  
train_data = data[0:120]
test_data = data[120:]
train_target = target[0:120]
test_target = target[120:]

# 展示数据
flowerName = ['Setosa', 'Versicolour', 'Virginica']
featureName = ['sepal length', 'sepal width', 'petal length', 'petal width']
data[0:5]
target[0:5]


# 加载数据集
dataset_train = TensorDataset(train_data, train_target)
dataloader_train = DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True)
dataset_test = TensorDataset(test_data, test_target)
dataloader_test = DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=True)


# 创建感知器模型
class irisClassify(nn.Module): 
    def __init__(self): 
        super().__init__()
        self.module = Sequential(
            Linear(4, 10), 
            ReLU(), 
            Linear(10, 3), 
            Softmax(dim=1)
        )
    def forward(self, data): 
        output = self.module(data)
        return output
  

iris_recognition_module = irisClassify()
loss = nn.CrossEntropyLoss()
optim = torch.optim.SGD(iris_recognition_module.parameters(), lr=LR)


# 测试模型
input = torch.tensor([[1, 2, 3, 4]], dtype=torch.float32)
input.shape
output = iris_recognition_module(input)
output.dtype


iterator = 0
bestRightNum = 0
for epoch in range(EPOCH): 
    # 训练模型
    _ = iris_recognition_module.train();
    print(f"------第{epoch}次训练------")
    for data in dataloader_train:
        trainData, trainTarget = data
        output = iris_recognition_module(trainData)
        loss_out = loss(output, trainTarget)
        optim.zero_grad()
        loss_out.backward()
        optim.step()
        if iterator % 10 == 0: 
            print(f"第{iterator}次的损失为: {loss_out:.6f}")
        iterator += 1
    # 测试模型
    _ = iris_recognition_module.eval();
    with torch.no_grad():
        total_right = 0
        for data in dataloader_test: 
            testData, testTraget = data    # (6, 4), (6,)
            output = iris_recognition_module(testData)
            right = (output.argmax(1) == testTraget).sum()
            total_right += right
        print(f"第{epoch}次训练的准确率为:  {1.0 * total_right / len(test_data):.3f}")
        if total_right >= bestRightNum or epoch == EPOCH - 1:
            bestRightNum = total_right
            path = f"./models/iris_reiris_recognition_model_{epoch}.pth"
            torch.save(iris_recognition_module.state_dict(), path)
        
        
# 手动测试模型是否正确

#4.6,3.2,1.4,0.2,Iris-setosa
#5.3,3.7,1.5,0.2,Iris-setosa
#5.0,3.3,1.4,0.2,Iris-setosa
#7.0,3.2,4.7,1.4,Iris-versicolor
#6.4,3.2,4.5,1.5,Iris-versicolor

#5.1,2.5,3.0,1.1,Iris-versicolor
#5.7,2.8,4.1,1.3,Iris-versicolor
#6.3,3.3,6.0,2.5,Iris-virginica
#5.8,2.7,5.1,1.9,Iris-virginica
#7.1,3.0,5.9,2.1,Iris-virginica

with torch.no_grad(): 
    testData1 = torch.tensor([[4.6,3.2,1.4,0.2],
                             [5.3,3.7,1.5,0.2],
                             [5.0,3.3,1.4,0.2], 
                             [7.0,3.2,4.7,1.4], 
                             [6.4,3.2,4.5,1.5]], dtype=torch.float32)
    testTarget1 = [0, 0, 0, 1, 1]
    predict1 = iris_recognition_module(testData1)
    predict1 = predict1.argmax(1)
    predict1
    testData2 = torch.tensor([[5.1,2.5,3.0,1.1],
                             [5.7,2.8,4.1,1.3],
                             [6.3,3.3,6.0,2.5], 
                             [5.8,2.7,5.1,1.9], 
                             [7.1,3.0,5.9,2.1]], dtype=torch.float32)
    testTarget2 = [1, 1, 2, 2, 2]
    predict2 = iris_recognition_module(testData2)
    predict2 = predict2.argmax(1)
    predict2
    testData3 = torch.tensor(  [[10, 10, 100, 10],
                                [20, 20, 200, 20], 
                                [5.1,2.5,3.0,1.1], 
                                [78, 46, 90, 1], 
                                [1200, 3400, 8900, 1200]], dtype=torch.float32)
    # 这4项数据都是凭空捏造的,应该均不属于这三类
    predict3 = iris_recognition_module(testData3)
    predict3 = predict3.argmax(1)
    predict3
2. 神经网络训练模型2.ipynb
import pandas as pd 
import sklearn
import torch
import torch.nn as nn
import numpy as np
from sklearn import datasets
from torchvision import transforms
from torch.nn import Linear, Sequential, ReLU, Softmax
from torch.utils.data import TensorDataset, DataLoader


from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity='all'


EPOCH = 100
BATCH_SIZE = 6
LR = 0.02


# 读取数据集
iris = datasets.load_iris()
data = iris.data
target = iris.target

# 对数据打乱
np.random.seed(116)
indices = np.arange(data.shape[0])
np.random.shuffle(indices)
data = data[indices]
target = target[indices]

# 转换为tensor, .float()转换为float32,因为Linear()默认为float32
data = torch.from_numpy(data).float()         # 150 * 4
# target需要保证为long类型
target = torch.from_numpy(target).long()  
train_data = data[0:120]
test_data = data[120:]
train_target = target[0:120]
test_target = target[120:]

# 反类训练数据加入
torch.manual_seed(42)    # 保证运行产生随机数相同
other_data = torch.randint(low=20, high=100, size=(100, 4)).float()
other_train_data = other_data[0:80]
other_test_data = other_data[80:]
train_data = torch.vstack((train_data, other_train_data))
test_data = torch.vstack((test_data, other_test_data))
train_target = torch.cat((train_target, (3 * torch.ones(80)).long()))
test_target = torch.cat((test_target, (3 * torch.ones(20)).long()))

# 展示数据
flowerName = ['Setosa', 'Versicolour', 'Virginica']
featureName = ['sepal length', 'sepal width', 'petal length', 'petal width']
data[0:5]
target[0:5]


# 加载数据集
dataset_train = TensorDataset(train_data, train_target)
dataloader_train = DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True)
dataset_test = TensorDataset(test_data, test_target)
dataloader_test = DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=True)


# 创建感知器模型
class irisClassify(nn.Module): 
    def __init__(self): 
        super().__init__()
        self.module = Sequential(
            Linear(4, 10), 
            ReLU(), 
            Linear(10, 4), 
            Softmax(dim=1)
        )
    def forward(self, data): 
        output = self.module(data)
        return output
    
    
iris_recognition_module = irisClassify()
loss = nn.CrossEntropyLoss()
optim = torch.optim.SGD(iris_recognition_module.parameters(), lr=LR)


# 测试模型
input = torch.tensor([[1, 2, 3, 4]], dtype=torch.float32)
input.shape
output = iris_recognition_module(input)
output.dtype


iterator = 0
bestRightNum = 0
for epoch in range(EPOCH): 
    # 训练模型
    _ = iris_recognition_module.train();
    print(f"------第{epoch}次训练------")
    for data in dataloader_train:
        trainData, trainTarget = data
        output = iris_recognition_module(trainData)
        loss_out = loss(output, trainTarget)
        optim.zero_grad()
        loss_out.backward()
        optim.step()
        if iterator % 10 == 0: 
            print(f"第{iterator}次的损失为: {loss_out:.6f}")
        iterator += 1
    # 测试模型
    _ = iris_recognition_module.eval();
    with torch.no_grad():
        total_right = 0
        for data in dataloader_test: 
            testData, testTraget = data    # (6, 4), (6,)
            output = iris_recognition_module(testData)
            right = (output.argmax(1) == testTraget).sum()
            total_right += right
        print(f"第{epoch}次训练的准确率为:  {1.0 * total_right / len(test_data):.3f}")
        if total_right >= bestRightNum or epoch == EPOCH - 1:
            bestRightNum = total_right
            path = f"./4_models/iris_reiris_recognition_model_{epoch}.pth"
            torch.save(iris_recognition_module.state_dict(), path)
        
        
# 手动测试模型是否正确

#4.6,3.2,1.4,0.2,Iris-setosa
#5.3,3.7,1.5,0.2,Iris-setosa
#5.0,3.3,1.4,0.2,Iris-setosa
#7.0,3.2,4.7,1.4,Iris-versicolor
#6.4,3.2,4.5,1.5,Iris-versicolor

#5.1,2.5,3.0,1.1,Iris-versicolor
#5.7,2.8,4.1,1.3,Iris-versicolor
#6.3,3.3,6.0,2.5,Iris-virginica
#5.8,2.7,5.1,1.9,Iris-virginica
#7.1,3.0,5.9,2.1,Iris-virginica

with torch.no_grad(): 
    testData1 = torch.tensor([[4.6,3.2,1.4,0.2],
                             [5.3,3.7,1.5,0.2],
                             [5.0,3.3,1.4,0.2], 
                             [7.0,3.2,4.7,1.4], 
                             [6.4,3.2,4.5,1.5]], dtype=torch.float32)
    testTarget1 = [0, 0, 0, 1, 1]
    predict1 = iris_recognition_module(testData1)
    predict1 = predict1.argmax(1)
    predict1
    testData2 = torch.tensor([[5.1,2.5,3.0,1.1],
                             [5.7,2.8,4.1,1.3],
                             [6.3,3.3,6.0,2.5], 
                             [5.8,2.7,5.1,1.9], 
                             [7.1,3.0,5.9,2.1]], dtype=torch.float32)
    testTarget2 = [1, 1, 2, 2, 2]
    predict2 = iris_recognition_module(testData2)
    predict2 = predict2.argmax(1)
    predict2
    testData3 = torch.tensor(  [[10, 10, 100, 10],
                                [20, 20, 200, 20], 
                                [5.1,2.5,3.0,1.1], 
                                [78, 46, 90, 80], 
                                [1200, 3400, 8900, 1200]], dtype=torch.float32)
    # 这4项数据都是凭空捏造的,应该均不属于这三类
    predict3 = iris_recognition_module(testData3)
    predict3 = predict3.argmax(1)
    predict3
3. GUI.ipynb
import tkinter as tk
import torch
import torch.nn as nn
from tkinter import messagebox
from torch.nn import Linear, Sequential, ReLU, Softmax
from PIL import Image, ImageTk


# 创建感知器模型
class irisClassify(nn.Module): 
    def __init__(self): 
        super().__init__()
        self.module = Sequential(
            Linear(4, 10), 
            ReLU(), 
            Linear(10, 4), 
            Softmax(dim=1)
        )
    def forward(self, data): 
        output = self.module(data)
        return output
    
    
def load_images():
    images = []
    imagePath = ['./iris_image/iris_setosa.png', './iris_image/iris_versicolor.png', './iris_image/iris_virginica .png', './iris_image/iris.png']
    for i in range(4):
        img = Image.open(imagePath[i])
        img = img.resize((276, 250), Image.Resampling.LANCZOS)  # 将图片调整为200x200大小
        images.append(ImageTk.PhotoImage(img))
    return images

def process_inputs(input, model):
    output = 0
    with torch.no_grad(): 
        input = torch.tensor(input, dtype = torch.float32).reshape(1, 4)
        output = model(input).argmax(1)[0]
    return output


def on_confirm(entries, result_label, images, descriptions, description_label, model):
    try:
        input_values = [float(entry.get()) for entry in entries]
        result = process_inputs(input_values, model)

        result_label.config(image=images[result])
        result_label.image = images[result]  # 保持对图片的引用

        # 更新描述标签
        description_label.config(text=descriptions[result])
    except ValueError:
        messagebox.showerror("输入错误", "请确保所有输入均为数字")

    # 运行主循环
    
    
def load_GUI(model):
    root = tk.Tk()
    root.title("鸢尾花分类器")
    root.geometry("600x500")  # 指定窗口大小
    
    # 创建一个框架用于布局
    frame = tk.Frame(root)
    frame.pack(pady=10)

    # 输入框提示文本
    labels_text = ["sepal length(萼片长,cm):", "sepal width(萼片宽,cm):", "petal length(花瓣长,cm):", "petal width(花瓣宽,cm):"]
    
    # 创建输入框和对应的提示标签
    entries = []  # 用于存储输入框的引用
    for i, text in enumerate(labels_text):
        label = tk.Label(frame, text=text)
        label.grid(row=i, column=0, padx=5, pady=5, sticky="e")  # 标签在输入框左侧
        entry = tk.Entry(frame)
        entry.grid(row=i, column=1, padx=5, pady=5)  # 输入框在标签右侧
        entries.append(entry)
    
    # 确定按钮放在输入框右边
    confirm_button = tk.Button(frame, text="确定", command=lambda: on_confirm(entries, result_label, images, descriptions, description_label, model))
    confirm_button.grid(row=0, column=2, rowspan=4, padx=5, pady=5)
    
    # 用于显示结果的标签和描述标签
    result_label = tk.Label(root)
    result_label.pack(pady=20)
    
    description_label = tk.Label(root)
    description_label.pack(pady=5)
    
    # 图片列表和对应描述
    images = []
    descriptions = [
        "Iris Setosa",  # 替换为相应图片的描述
        "Iris Versicolour",
        "Iris Virginica",
        "is not Iris",
    ]
    images = load_images()
    root.mainloop()
def load_model(path): 
    par = torch.load(path)
    iris_recognition_model = irisClassify()
    iris_recognition_model.load_state_dict(par)
    return iris_recognition_model


def main(): 
    model_path = './4_models/iris_reiris_recognition_model_99.pth'
    iris_recognition_model = load_model(model_path)
    load_GUI(iris_recognition_model)
if __name__=="__main__":
    main()
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
PyTorch是一个基于Python的开源机器学习库,它提供了丰富的工具和函数来简化深度学习模型的开发和训练过程。下面是使用PyTorch实现鸢尾花分类的一般步骤: 1. 导入必要的库和模块: ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, Dataset ``` 2. 准备数据集: 首先,你需要准备鸢尾花数据集。可以使用`torchvision.datasets`中的`datasets`模块来加载常见的数据集,或者自己创建一个继承自`torch.utils.data.Dataset`的数据集类。 3. 创建模型: 定义一个继承自`torch.nn.Module`的模型类,并在其中定义模型的结构。可以使用`torch.nn`中的各种层(如全连接层、卷积层等)来构建模型。 4. 定义损失函数和优化器: 选择适当的损失函数(如交叉熵损失)和优化器(如随机梯度下降SGD)来训练模型。 5. 训练模型: 使用训练数据对模型进行训练。通过迭代训练数据集中的样本,计算损失并反向传播更新模型参数。 6. 模型评估: 使用测试数据对训练好的模型进行评估,计算模型在测试集上的准确率或其他指标。 下面是一个简单的示例代码,演示了如何使用PyTorch实现鸢尾花分类: ```python import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, Dataset from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 准备数据集 iris = load_iris() X = iris.data y = iris.target # 数据预处理 scaler = StandardScaler() X = scaler.fit_transform(X) # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 定义数据集类 class IrisDataset(Dataset): def __init__(self, X, y): self.X = torch.tensor(X, dtype=torch.float32) self.y = torch.tensor(y, dtype=torch.long) def __len__(self): return len(self.X) def __getitem__(self, idx): return self.X[idx], self.y[idx] # 创建模型类 class IrisClassifier(nn.Module): def __init__(self): super(IrisClassifier, self).__init__() self.fc1 = nn.Linear(4, 16) self.fc2 = nn.Linear(16, 3) def forward(self, x): x = torch.relu(self.fc1(x)) x = self.fc2(x) return x # 创建数据加载器 train_dataset = IrisDataset(X_train, y_train) train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True) # 创建模型和优化器 model = IrisClassifier() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.01) # 训练模型 num_epochs = 100 for epoch in range(num_epochs): for inputs, labels in train_loader: optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # 模型评估 test_dataset = IrisDataset(X_test, y_test) test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False) correct = 0 total = 0 with torch.no_grad(): for inputs, labels in test_loader: outputs = model(inputs) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() accuracy = correct / total print(f"Accuracy: {accuracy}") ``` 这是一个简单的示例,你可以根据需要进行修改和扩展。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Febu4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值