├── LICENSE ├── README.md └── ONNX2.ipynb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 mahdieslaminet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MNIST_ONNX_Classification 2 | 3 | This repository contains a Python project for training a simple neural network on the MNIST dataset using PyTorch, exporting the model to ONNX format, and performing image classification using ONNX Runtime. The project includes steps to upload and utilize the ONNX model stored on Google Drive. 4 | 5 | ## Requirements 6 | 7 | Make sure to install the following libraries before running the project: 8 | 9 | ```bash 10 | pip install torch torchvision onnx onnxruntime requests matplotlib numpy 11 | ``` 12 | 13 | ## Project Structure 14 | 15 | - `train_and_save_model.py`: Script for training a simple neural network on the MNIST dataset and saving the model in ONNX format to Google Drive. 16 | - `classify_images.py`: Script for loading the ONNX model from Google Drive and classifying images from the MNIST dataset. 17 | 18 | ## Usage 19 | 20 | ### 1. Training and Saving the Model 21 | 22 | This script trains a simple neural network on the MNIST dataset and saves the trained model in ONNX format to your Google Drive. 23 | 24 | ```python 25 | from google.colab import drive 26 | drive.mount('/content/drive', force_remount=True) 27 | 28 | import torch 29 | import torch.nn as nn 30 | import torch.optim as optim 31 | from torchvision import datasets, transforms 32 | import onnx 33 | 34 | # Define a simple neural network 35 | class SimpleModel(nn.Module): 36 | def __init__(self): 37 | super(SimpleModel, self).__init__() 38 | self.fc1 = nn.Linear(28 * 28, 128) 39 | self.fc2 = nn.Linear(128, 10) 40 | 41 | def forward(self, x): 42 | x = x.view(-1, 28 * 28) 43 | x = torch.relu(self.fc1(x)) 44 | x = self.fc2(x) 45 | return x 46 | 47 | # Prepare the MNIST dataset 48 | transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) 49 | train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform) 50 | train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True) 51 | 52 | # Initialize the model, loss function, and optimizer 53 | model = SimpleModel() 54 | criterion = nn.CrossEntropyLoss() 55 | optimizer = optim.SGD(model.parameters(), lr=0.01) 56 | 57 | # Train the model 58 | for epoch in range(5): # Train for 5 epochs 59 | for images, labels in train_loader: 60 | outputs = model(images) 61 | loss = criterion(outputs, labels) 62 | 63 | optimizer.zero_grad() 64 | loss.backward() 65 | optimizer.step() 66 | 67 | print(f'Epoch {epoch+1}, Loss: {loss.item()}') 68 | 69 | # Convert the trained model to ONNX format and save to Google Drive 70 | dummy_input = torch.randn(1, 1, 28, 28) # Example input for the model 71 | onnx_model_path = "/content/drive/My Drive/simple_model.onnx" 72 | torch.onnx.export(model, dummy_input, onnx_model_path, input_names=['input'], output_names=['output']) 73 | print(f"Model saved to {onnx_model_path}") 74 | ``` 75 | 76 | ### 2. Classifying Images 77 | 78 | This script loads the ONNX model from Google Drive and performs classification on images from the MNIST dataset. 79 | 80 | ```python 81 | import requests 82 | import onnxruntime as ort 83 | import numpy as np 84 | import matplotlib.pyplot as plt 85 | from torchvision import datasets, transforms 86 | import torch 87 | from google.colab import drive 88 | 89 | # Mount Google Drive 90 | drive.mount('/content/drive', force_remount=True) 91 | 92 | # Path to the ONNX model on Google Drive 93 | onnx_model_path = "/content/drive/My Drive/simple_model.onnx" 94 | 95 | # Load the ONNX model 96 | ort_session = ort.InferenceSession(onnx_model_path) 97 | 98 | # Prepare the MNIST dataset 99 | transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))]) 100 | test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform) 101 | test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=1, shuffle=True) 102 | 103 | # Select a few images from the dataset 104 | images, labels = [], [] 105 | for i, (img, lbl) in enumerate(test_loader): 106 | if i >= 5: 107 | break 108 | images.append(img) 109 | labels.append(lbl) 110 | 111 | images = torch.cat(images) 112 | labels = torch.cat(labels) 113 | 114 | # Display images and perform classification 115 | plt.figure(figsize=(10, 5)) 116 | 117 | for i in range(5): 118 | image = images[i].numpy().squeeze() 119 | label = labels[i].item() 120 | 121 | # Prepare the input for the ONNX model 122 | ort_inputs = {ort_session.get_inputs()[0].name: images[i].numpy().reshape(1, 1, 28, 28).astype(np.float32)} 123 | ort_outs = ort_session.run(None, ort_inputs) 124 | pred_label = np.argmax(ort_outs[0]) 125 | 126 | # Display the image 127 | plt.subplot(1, 5, i+1) 128 | plt.imshow(image, cmap='gray') 129 | plt.title(f'Label: {label}\nPred: {pred_label}') 130 | plt.axis('off') 131 | 132 | # Show result of classification 133 | if label == pred_label: 134 | plt.xlabel('Correct', color='green') 135 | else: 136 | plt.xlabel('Wrong', color='red') 137 | 138 | plt.show() 139 | ``` 140 | 141 | ## Functions 142 | 143 | ### `train_and_save_model.py` 144 | 145 | - `SimpleModel`: Defines a simple fully connected neural network. 146 | - `train_model`: Trains the model on the MNIST dataset. 147 | - `save_model_to_onnx`: Converts the trained model to ONNX format and saves it to Google Drive. 148 | 149 | ### `classify_images.py` 150 | 151 | - `load_model`: Loads the ONNX model from Google Drive. 152 | - `classify_images`: Classifies images from the MNIST dataset using the ONNX model. 153 | - `display_results`: Displays images along with classification results. 154 | 155 | ## File Storage 156 | 157 | - The trained ONNX model is saved in your Google Drive at the path: `/content/drive/My Drive/simple_model.onnx`. 158 | 159 | ## References 160 | 161 | - [PyTorch Documentation](https://pytorch.org/docs/stable/index.html) 162 | - [ONNX Documentation](https://onnx.ai/) 163 | - [ONNX Runtime Documentation](https://onnxruntime.ai/) 164 | 165 | ## Similar Projects 166 | 167 | - [PyTorch to ONNX Conversion](https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html) 168 | - [ONNX Runtime GitHub](https://github.com/microsoft/onnxruntime) 169 | 170 | Feel free to clone this repository and use it for your own projects. Contributions and suggestions are welcome! 171 | -------------------------------------------------------------------------------- /ONNX2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [], 7 | "mount_file_id": "1ZWr1G0NtcOiy9tu1y9IdjCTcnbx-aFdo", 8 | "authorship_tag": "ABX9TyMiJ6Hx7oQJsCXtDb0QFuSE", 9 | "include_colab_link": true 10 | }, 11 | "kernelspec": { 12 | "name": "python3", 13 | "display_name": "Python 3" 14 | }, 15 | "language_info": { 16 | "name": "python" 17 | } 18 | }, 19 | "cells": [ 20 | { 21 | "cell_type": "markdown", 22 | "metadata": { 23 | "id": "view-in-github", 24 | "colab_type": "text" 25 | }, 26 | "source": [ 27 | "\"Open" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "source": [ 33 | "pip install torch onnx onnxruntime\n" 34 | ], 35 | "metadata": { 36 | "colab": { 37 | "base_uri": "https://localhost:8080/" 38 | }, 39 | "id": "78lWrV_1fWQa", 40 | "outputId": "49e36e95-b5f4-44fc-8d42-b1e0eb66ed03" 41 | }, 42 | "execution_count": null, 43 | "outputs": [ 44 | { 45 | "output_type": "stream", 46 | "name": "stdout", 47 | "text": [ 48 | "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (2.3.1+cu121)\n", 49 | "Collecting onnx\n", 50 | " Downloading onnx-1.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (16 kB)\n", 51 | "Collecting onnxruntime\n", 52 | " Downloading onnxruntime-1.18.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (4.3 kB)\n", 53 | "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch) (3.15.4)\n", 54 | "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.10/dist-packages (from torch) (4.12.2)\n", 55 | "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch) (1.13.1)\n", 56 | "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch) (3.3)\n", 57 | "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch) (3.1.4)\n", 58 | "Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from torch) (2024.6.1)\n", 59 | "Collecting nvidia-cuda-nvrtc-cu12==12.1.105 (from torch)\n", 60 | " Using cached nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl.metadata (1.5 kB)\n", 61 | "Collecting nvidia-cuda-runtime-cu12==12.1.105 (from torch)\n", 62 | " Using cached nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl.metadata (1.5 kB)\n", 63 | "Collecting nvidia-cuda-cupti-cu12==12.1.105 (from torch)\n", 64 | " Using cached nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl.metadata (1.6 kB)\n", 65 | "Collecting nvidia-cudnn-cu12==8.9.2.26 (from torch)\n", 66 | " Using cached nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl.metadata (1.6 kB)\n", 67 | "Collecting nvidia-cublas-cu12==12.1.3.1 (from torch)\n", 68 | " Using cached nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl.metadata (1.5 kB)\n", 69 | "Collecting nvidia-cufft-cu12==11.0.2.54 (from torch)\n", 70 | " Using cached nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl.metadata (1.5 kB)\n", 71 | "Collecting nvidia-curand-cu12==10.3.2.106 (from torch)\n", 72 | " Using cached nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl.metadata (1.5 kB)\n", 73 | "Collecting nvidia-cusolver-cu12==11.4.5.107 (from torch)\n", 74 | " Using cached nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl.metadata (1.6 kB)\n", 75 | "Collecting nvidia-cusparse-cu12==12.1.0.106 (from torch)\n", 76 | " Using cached nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl.metadata (1.6 kB)\n", 77 | "Collecting nvidia-nccl-cu12==2.20.5 (from torch)\n", 78 | " Using cached nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl.metadata (1.8 kB)\n", 79 | "Collecting nvidia-nvtx-cu12==12.1.105 (from torch)\n", 80 | " Using cached nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl.metadata (1.7 kB)\n", 81 | "Requirement already satisfied: triton==2.3.1 in /usr/local/lib/python3.10/dist-packages (from torch) (2.3.1)\n", 82 | "Collecting nvidia-nvjitlink-cu12 (from nvidia-cusolver-cu12==11.4.5.107->torch)\n", 83 | " Using cached nvidia_nvjitlink_cu12-12.6.20-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\n", 84 | "Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.10/dist-packages (from onnx) (1.26.4)\n", 85 | "Requirement already satisfied: protobuf>=3.20.2 in /usr/local/lib/python3.10/dist-packages (from onnx) (3.20.3)\n", 86 | "Collecting coloredlogs (from onnxruntime)\n", 87 | " Downloading coloredlogs-15.0.1-py2.py3-none-any.whl.metadata (12 kB)\n", 88 | "Requirement already satisfied: flatbuffers in /usr/local/lib/python3.10/dist-packages (from onnxruntime) (24.3.25)\n", 89 | "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from onnxruntime) (24.1)\n", 90 | "Collecting humanfriendly>=9.1 (from coloredlogs->onnxruntime)\n", 91 | " Downloading humanfriendly-10.0-py2.py3-none-any.whl.metadata (9.2 kB)\n", 92 | "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch) (2.1.5)\n", 93 | "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy->torch) (1.3.0)\n", 94 | "Using cached nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl (410.6 MB)\n", 95 | "Using cached nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (14.1 MB)\n", 96 | "Using cached nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (23.7 MB)\n", 97 | "Using cached nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (823 kB)\n", 98 | "Using cached nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl (731.7 MB)\n", 99 | "Using cached nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl (121.6 MB)\n", 100 | "Using cached nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl (56.5 MB)\n", 101 | "Using cached nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl (124.2 MB)\n", 102 | "Using cached nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl (196.0 MB)\n", 103 | "Using cached nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl (176.2 MB)\n", 104 | "Using cached nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl (99 kB)\n", 105 | "Downloading onnx-1.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (15.9 MB)\n", 106 | "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m15.9/15.9 MB\u001b[0m \u001b[31m20.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 107 | "\u001b[?25hDownloading onnxruntime-1.18.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (6.8 MB)\n", 108 | "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.8/6.8 MB\u001b[0m \u001b[31m81.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 109 | "\u001b[?25hDownloading coloredlogs-15.0.1-py2.py3-none-any.whl (46 kB)\n", 110 | "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.0/46.0 kB\u001b[0m \u001b[31m3.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 111 | "\u001b[?25hDownloading humanfriendly-10.0-py2.py3-none-any.whl (86 kB)\n", 112 | "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.8/86.8 kB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", 113 | "\u001b[?25hUsing cached nvidia_nvjitlink_cu12-12.6.20-py3-none-manylinux2014_x86_64.whl (19.7 MB)\n", 114 | "Installing collected packages: onnx, nvidia-nvtx-cu12, nvidia-nvjitlink-cu12, nvidia-nccl-cu12, nvidia-curand-cu12, nvidia-cufft-cu12, nvidia-cuda-runtime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, humanfriendly, nvidia-cusparse-cu12, nvidia-cudnn-cu12, coloredlogs, onnxruntime, nvidia-cusolver-cu12\n", 115 | "Successfully installed coloredlogs-15.0.1 humanfriendly-10.0 nvidia-cublas-cu12-12.1.3.1 nvidia-cuda-cupti-cu12-12.1.105 nvidia-cuda-nvrtc-cu12-12.1.105 nvidia-cuda-runtime-cu12-12.1.105 nvidia-cudnn-cu12-8.9.2.26 nvidia-cufft-cu12-11.0.2.54 nvidia-curand-cu12-10.3.2.106 nvidia-cusolver-cu12-11.4.5.107 nvidia-cusparse-cu12-12.1.0.106 nvidia-nccl-cu12-2.20.5 nvidia-nvjitlink-cu12-12.6.20 nvidia-nvtx-cu12-12.1.105 onnx-1.16.2 onnxruntime-1.18.1\n" 116 | ] 117 | } 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "source": [ 123 | "pip install torch torchvision onnx onnxruntime\n" 124 | ], 125 | "metadata": { 126 | "colab": { 127 | "base_uri": "https://localhost:8080/" 128 | }, 129 | "id": "vSulfPATfnHn", 130 | "outputId": "92a0411b-1fb2-469f-f4eb-9c82419008e8" 131 | }, 132 | "execution_count": null, 133 | "outputs": [ 134 | { 135 | "output_type": "stream", 136 | "name": "stdout", 137 | "text": [ 138 | "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (2.3.1+cu121)\n", 139 | "Requirement already satisfied: torchvision in /usr/local/lib/python3.10/dist-packages (0.18.1+cu121)\n", 140 | "Requirement already satisfied: onnx in /usr/local/lib/python3.10/dist-packages (1.16.2)\n", 141 | "Requirement already satisfied: onnxruntime in /usr/local/lib/python3.10/dist-packages (1.18.1)\n", 142 | "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch) (3.15.4)\n", 143 | "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.10/dist-packages (from torch) (4.12.2)\n", 144 | "Requirement already satisfied: sympy in /usr/local/lib/python3.10/dist-packages (from torch) (1.13.1)\n", 145 | "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch) (3.3)\n", 146 | "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch) (3.1.4)\n", 147 | "Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from torch) (2024.6.1)\n", 148 | "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch) (12.1.105)\n", 149 | "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch) (12.1.105)\n", 150 | "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch) (12.1.105)\n", 151 | "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /usr/local/lib/python3.10/dist-packages (from torch) (8.9.2.26)\n", 152 | "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /usr/local/lib/python3.10/dist-packages (from torch) (12.1.3.1)\n", 153 | "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /usr/local/lib/python3.10/dist-packages (from torch) (11.0.2.54)\n", 154 | "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /usr/local/lib/python3.10/dist-packages (from torch) (10.3.2.106)\n", 155 | "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /usr/local/lib/python3.10/dist-packages (from torch) (11.4.5.107)\n", 156 | "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /usr/local/lib/python3.10/dist-packages (from torch) (12.1.0.106)\n", 157 | "Requirement already satisfied: nvidia-nccl-cu12==2.20.5 in /usr/local/lib/python3.10/dist-packages (from torch) (2.20.5)\n", 158 | "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /usr/local/lib/python3.10/dist-packages (from torch) (12.1.105)\n", 159 | "Requirement already satisfied: triton==2.3.1 in /usr/local/lib/python3.10/dist-packages (from torch) (2.3.1)\n", 160 | "Requirement already satisfied: nvidia-nvjitlink-cu12 in /usr/local/lib/python3.10/dist-packages (from nvidia-cusolver-cu12==11.4.5.107->torch) (12.6.20)\n", 161 | "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (from torchvision) (1.26.4)\n", 162 | "Requirement already satisfied: pillow!=8.3.*,>=5.3.0 in /usr/local/lib/python3.10/dist-packages (from torchvision) (9.4.0)\n", 163 | "Requirement already satisfied: protobuf>=3.20.2 in /usr/local/lib/python3.10/dist-packages (from onnx) (3.20.3)\n", 164 | "Requirement already satisfied: coloredlogs in /usr/local/lib/python3.10/dist-packages (from onnxruntime) (15.0.1)\n", 165 | "Requirement already satisfied: flatbuffers in /usr/local/lib/python3.10/dist-packages (from onnxruntime) (24.3.25)\n", 166 | "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from onnxruntime) (24.1)\n", 167 | "Requirement already satisfied: humanfriendly>=9.1 in /usr/local/lib/python3.10/dist-packages (from coloredlogs->onnxruntime) (10.0)\n", 168 | "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch) (2.1.5)\n", 169 | "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy->torch) (1.3.0)\n" 170 | ] 171 | } 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "metadata": { 178 | "colab": { 179 | "base_uri": "https://localhost:8080/" 180 | }, 181 | "id": "2Vx1rkfuZp0-", 182 | "outputId": "957db4f4-352f-49bb-8274-b13e6d0374bb" 183 | }, 184 | "outputs": [ 185 | { 186 | "output_type": "stream", 187 | "name": "stdout", 188 | "text": [ 189 | "Mounted at /content/drive\n", 190 | "Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\n", 191 | "Failed to download (trying next):\n", 192 | "HTTP Error 403: Forbidden\n", 193 | "\n", 194 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz\n", 195 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-images-idx3-ubyte.gz to ./data/MNIST/raw/train-images-idx3-ubyte.gz\n" 196 | ] 197 | }, 198 | { 199 | "output_type": "stream", 200 | "name": "stderr", 201 | "text": [ 202 | "100%|██████████| 9912422/9912422 [00:10<00:00, 968941.41it/s] \n" 203 | ] 204 | }, 205 | { 206 | "output_type": "stream", 207 | "name": "stdout", 208 | "text": [ 209 | "Extracting ./data/MNIST/raw/train-images-idx3-ubyte.gz to ./data/MNIST/raw\n", 210 | "\n", 211 | "Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz\n", 212 | "Failed to download (trying next):\n", 213 | "HTTP Error 403: Forbidden\n", 214 | "\n", 215 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz\n", 216 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/train-labels-idx1-ubyte.gz to ./data/MNIST/raw/train-labels-idx1-ubyte.gz\n" 217 | ] 218 | }, 219 | { 220 | "output_type": "stream", 221 | "name": "stderr", 222 | "text": [ 223 | "100%|██████████| 28881/28881 [00:00<00:00, 152313.89it/s]\n" 224 | ] 225 | }, 226 | { 227 | "output_type": "stream", 228 | "name": "stdout", 229 | "text": [ 230 | "Extracting ./data/MNIST/raw/train-labels-idx1-ubyte.gz to ./data/MNIST/raw\n", 231 | "\n", 232 | "Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz\n", 233 | "Failed to download (trying next):\n", 234 | "HTTP Error 403: Forbidden\n", 235 | "\n", 236 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz\n", 237 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-images-idx3-ubyte.gz to ./data/MNIST/raw/t10k-images-idx3-ubyte.gz\n" 238 | ] 239 | }, 240 | { 241 | "output_type": "stream", 242 | "name": "stderr", 243 | "text": [ 244 | "100%|██████████| 1648877/1648877 [00:01<00:00, 1445960.74it/s]\n" 245 | ] 246 | }, 247 | { 248 | "output_type": "stream", 249 | "name": "stdout", 250 | "text": [ 251 | "Extracting ./data/MNIST/raw/t10k-images-idx3-ubyte.gz to ./data/MNIST/raw\n", 252 | "\n", 253 | "Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz\n", 254 | "Failed to download (trying next):\n", 255 | "HTTP Error 403: Forbidden\n", 256 | "\n", 257 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz\n", 258 | "Downloading https://ossci-datasets.s3.amazonaws.com/mnist/t10k-labels-idx1-ubyte.gz to ./data/MNIST/raw/t10k-labels-idx1-ubyte.gz\n" 259 | ] 260 | }, 261 | { 262 | "output_type": "stream", 263 | "name": "stderr", 264 | "text": [ 265 | "100%|██████████| 4542/4542 [00:00<00:00, 4477210.05it/s]\n" 266 | ] 267 | }, 268 | { 269 | "output_type": "stream", 270 | "name": "stdout", 271 | "text": [ 272 | "Extracting ./data/MNIST/raw/t10k-labels-idx1-ubyte.gz to ./data/MNIST/raw\n", 273 | "\n", 274 | "Epoch 1, Loss: 0.3986712098121643\n", 275 | "Epoch 2, Loss: 0.30953291058540344\n", 276 | "Epoch 3, Loss: 0.4542768895626068\n", 277 | "Epoch 4, Loss: 0.19958028197288513\n", 278 | "Epoch 5, Loss: 0.16435885429382324\n", 279 | "Model saved to /content/drive/My Drive/simple_model.onnx\n" 280 | ] 281 | } 282 | ], 283 | "source": [ 284 | "\n", 285 | "from google.colab import drive\n", 286 | "drive.mount('/content/drive', force_remount=True)\n", 287 | "\n", 288 | "\n", 289 | "# آموزش و تبدیل مدل به ONNX\n", 290 | "import torch\n", 291 | "import torch.nn as nn\n", 292 | "import torch.optim as optim\n", 293 | "from torchvision import datasets, transforms\n", 294 | "import onnx\n", 295 | "import onnxruntime as ort\n", 296 | "import numpy as np\n", 297 | "\n", 298 | "# تعریف مدل ساده\n", 299 | "class SimpleModel(nn.Module):\n", 300 | " def __init__(self):\n", 301 | " super(SimpleModel, self).__init__()\n", 302 | " self.fc1 = nn.Linear(28 * 28, 128)\n", 303 | " self.fc2 = nn.Linear(128, 10)\n", 304 | "\n", 305 | " def forward(self, x):\n", 306 | " x = x.view(-1, 28 * 28)\n", 307 | " x = torch.relu(self.fc1(x))\n", 308 | " x = self.fc2(x)\n", 309 | " return x\n", 310 | "\n", 311 | "# آماده‌سازی داده‌ها\n", 312 | "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n", 313 | "train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)\n", 314 | "train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=64, shuffle=True)\n", 315 | "\n", 316 | "# ساخت مدل، تعریف تابع هزینه و بهینه‌ساز\n", 317 | "model = SimpleModel()\n", 318 | "criterion = nn.CrossEntropyLoss()\n", 319 | "optimizer = optim.SGD(model.parameters(), lr=0.01)\n", 320 | "\n", 321 | "# آموزش مدل\n", 322 | "for epoch in range(5): # آموزش برای 5 دور\n", 323 | " for images, labels in train_loader:\n", 324 | " outputs = model(images)\n", 325 | " loss = criterion(outputs, labels)\n", 326 | "\n", 327 | " optimizer.zero_grad()\n", 328 | " loss.backward()\n", 329 | " optimizer.step()\n", 330 | "\n", 331 | " print(f'Epoch {epoch+1}, Loss: {loss.item()}')\n", 332 | "\n", 333 | "# تبدیل مدل به فرمت ONNX\n", 334 | "dummy_input = torch.randn(1, 1, 28, 28) # ورودی نمونه برای مدل\n", 335 | "onnx_model_path = \"/content/drive/My Drive/simple_model.onnx\"\n", 336 | "torch.onnx.export(model, dummy_input, onnx_model_path, input_names=['input'], output_names=['output'])\n", 337 | "print(f\"Model saved to {onnx_model_path}\")\n" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "source": [ 343 | "import requests\n", 344 | "import onnxruntime as ort\n", 345 | "import numpy as np\n", 346 | "import matplotlib.pyplot as plt\n", 347 | "from torchvision import datasets, transforms\n", 348 | "import torch\n", 349 | "from google.colab import drive\n", 350 | "\n", 351 | "# اتصال به گوگل درایو\n", 352 | "drive.mount('/content/drive', force_remount=True)\n", 353 | "\n", 354 | "# مسیر مدل ONNX در گوگل درایو\n", 355 | "onnx_model_path = \"/content/drive/My Drive/simple_model.onnx\"\n", 356 | "\n", 357 | "# بارگذاری مدل ONNX\n", 358 | "ort_session = ort.InferenceSession(onnx_model_path)\n", 359 | "\n", 360 | "# آماده‌سازی داده‌ها (دیتاست MNIST)\n", 361 | "transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])\n", 362 | "test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)\n", 363 | "test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=1, shuffle=True)\n", 364 | "\n", 365 | "# انتخاب چند تصویر از دیتاست\n", 366 | "images, labels = [], []\n", 367 | "for i, (img, lbl) in enumerate(test_loader):\n", 368 | " if i >= 5:\n", 369 | " break\n", 370 | " images.append(img)\n", 371 | " labels.append(lbl)\n", 372 | "\n", 373 | "images = torch.cat(images)\n", 374 | "labels = torch.cat(labels)\n", 375 | "\n", 376 | "# نمایش تصاویر و انجام کلاس‌بندی\n", 377 | "plt.figure(figsize=(10, 5))\n", 378 | "\n", 379 | "for i in range(5):\n", 380 | " image = images[i].numpy().squeeze()\n", 381 | " label = labels[i].item()\n", 382 | "\n", 383 | " # آماده‌سازی ورودی برای مدل ONNX\n", 384 | " ort_inputs = {ort_session.get_inputs()[0].name: images[i].numpy().reshape(1, 1, 28, 28).astype(np.float32)}\n", 385 | " ort_outs = ort_session.run(None, ort_inputs)\n", 386 | " pred_label = np.argmax(ort_outs[0])\n", 387 | "\n", 388 | " # نمایش تصویر\n", 389 | " plt.subplot(1, 5, i+1)\n", 390 | " plt.imshow(image, cmap='gray')\n", 391 | " plt.title(f'Label: {label}\\nPred: {pred_label}')\n", 392 | " plt.axis('off')\n", 393 | "\n", 394 | " # نمایش نتیجه درستی یا نادرستی\n", 395 | " if label == pred_label:\n", 396 | " plt.xlabel('Correct', color='green')\n", 397 | " else:\n", 398 | " plt.xlabel('Wrong', color='red')\n", 399 | "\n", 400 | "plt.show()\n" 401 | ], 402 | "metadata": { 403 | "colab": { 404 | "base_uri": "https://localhost:8080/", 405 | "height": 189 406 | }, 407 | "id": "EfFAKGIwZuQp", 408 | "outputId": "893b0a05-fe42-4a70-cd3d-4113d6a8a181" 409 | }, 410 | "execution_count": null, 411 | "outputs": [ 412 | { 413 | "output_type": "stream", 414 | "name": "stdout", 415 | "text": [ 416 | "Mounted at /content/drive\n" 417 | ] 418 | }, 419 | { 420 | "output_type": "display_data", 421 | "data": { 422 | "text/plain": [ 423 | "
" 424 | ], 425 | "image/png": "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\n" 426 | }, 427 | "metadata": {} 428 | } 429 | ] 430 | } 431 | ] 432 | } --------------------------------------------------------------------------------