人工智能入门(一):基于Pytorch的手写数字识别模型

前言:

因为还在上学,时间不太够用,很多内容写到后面心有余力不足,未来有时间我会慢慢补充。人工智能的知识涉猎范围广又杂乱无章,啃书或上课学到的知识往往很早就过时了或者离实际的项目无关。所以,我很希望通过这种一个一个“小实验”的方式让大家拎出一个属于自己的“主干知识”,尝到训练模型的“甜头”。

一、Python基础知识:

1 类与对象的关系:

比如定义一个“鸟类”。鹦鹉就是“鸟类”的一个对象,海鸥也是“鸟类”的一个对象。

“类”是静态的,是在程序执行前就已经被定义好的;

“对象”是动态的,它们在程序执行时可以被创建和删除。

2 类的属性:

class Bird:Flight_mode = '滑翔'Reproduction_mode = '蛋生'  #类的属性def introduce(self):   #类的方法print('爷是只高贵的鸟')

类的属性是全类共有的,比如上例中定义的“鸟类“里的”飞行方式“和”繁衍方式“;

类的方法可以简单理解成在类中写了一个函数,不过要注意的是:在Python中,self是一个表示“对象“自身的参数,通常作为方法的第一个参数。当我们创建一个对象时,Python会自动将该对象作为self参数传递给类的方法。

接下来,请读者思考一个问题,假如我有一只小鸟名叫”Lucky“,基于上面的例子,如果我想打印:

(1)“Lucky的飞行方式是滑翔,繁衍方式是蛋生”应该怎么做?

(2)“Lucky的年龄是一岁大,颜色是黄色的”上例中类的方法应该怎么修改?

解答:

(1)的问题比较简单,因为飞行方式和繁衍方式都是整个“鸟类”共同的属性,直接使用类名调用即可。

print("Lucky的飞行方式是"+Bird.Flight_mode+",繁衍方式是"+Bird.Reproduction_mode)

(2)最大的问题在于,年龄和颜色都是Lucky这个“对象”的属性,这时我们就需要使用类的方法来定义对象的属性。说白了就是在类中写一个函数,让函数中的属性只属于定义的对象。

class Bird:Flight_mode = '滑翔'Reproduction_mode = '蛋生'  # 类的属性def __init__(self, name, age, colour):self.name = nameself.age = ageself.colour = colourdef introduce(self):print(self.name,"的年龄是", self.age, ",颜色是", self.colour)p = Bird("Lucky", "一岁大", "黄色的")
p.introduce()

其中,__init__是一个特殊的方法,用于在创建对象时进行初始化。它是Python中的构造函数,用于将属性值赋给对象。

类的基础介绍言尽于此,不多赘述。

参考文章:http://t.csdnimg.cn/Eg9kr

3 类的继承:

继承的主要作用是实现代码的重用。继承使得子类拥有父类的方法和属性。在使用深度学习框架,比如Pytorch时,只有将我们的类继承官方的类,我们才能在官方的类实现的基础上继续做下去

class animal:def eat(self):print("吃")def drink(self):print("喝")class dog(animal):def dark(self):print("汪汪叫")goudan = dog()
goudan.eat()
goudan.drink()

从上面的代码可以看出,在编写dog类的时候,我们并没有重写eat和drink两个方法。我们只需要在dog后面的括号中加上父类的名字即可。当子类继承了父类,子类就可以直接使用父类中的方法了。在本例中,goudan可以直接使用animal类中的eat和drink两个方法。

参考文章:http://t.csdnimg.cn/sw0VK

二、简单科普:

1 深度学习框架的概念:

如何让计算机识别手写数字0~9?如今的我们只需要一个简单的卷积神经网络(CNN)就可以搞定。

本次我们使用pytorch深度学习框架,使用pytorch中的nn函数来调用深度学习中的基本单元,用pytorch中的nn.squential模块来构建神经网络模型。

深度学习框架的基本属性:

1.将繁琐的计算、操作封装成黑箱,让我们只需要关注输入与输出;

2.把解决问题的流程模块化、模板化;

3.常用的工具封装成库,方便使用。

所以,框架的概念就是模板的概念,就像英语作文模板往往会给你提供好开头、结尾等,让我们只需要根据不同的作文主题更换不同的英语单词一样。

2 张量的概念:

张量,英文为“tensor”,是神经网络的主要数据容器,它包含的数据几乎都是数值。但显然,我们传入神经网络的数据并不只有数字,还有图片、声音、文本等等。如何建立起他们之间的联系?这个我们稍后解释,现在我们先来看看什么是张量。

2.1 什么是张量:

学习过线性代数的话,你可能对矩阵比较熟悉,张量其实就是矩阵向任意维度的推广

(1)如果一个矩阵只有一个元素,那么它被称为0阶张量;

(2)如果一个矩阵只有一行或者一列(我们称其为只有一个“”),则被称为一阶张量;

(3)显然,大部分的矩阵都有m行n列(有两个轴),则就被称为二阶张量;

(4)如果将一个二阶张量看作一个整体,由多个二阶张量组成一个新的数组,则二阶张量就被推广为三阶张量;

bdd298739a984edbb862b385d2071dc7.png

当然,我本人有个小技巧帮助你推导三阶之后的张量。来,跟我念:一个张量、往下拉、往右拉、往后拉;看成一个整体、往下拉(四阶)、往右拉(五阶)、往后拉(六阶);看作一个整体.......以此类推。

当然,在实践中,我们很少遇到三阶之后的张量,所以也没必要对想象出高阶张量那么执着。

参考文章:http://t.csdnimg.cn/OjOqS

2.2 图片与张量的相互转换:

首先,我们来思考一种最简单的情况。假如有一张灰度图像,显然它是一个二维的平面,它由密密麻麻的像素组成。对于每一个像素点如果我们用由低到高的数字来代表像素点灰色的深度,那么显然,这张图片可以被一个二阶张量所表示;

对于一张彩色图像,实际上每一个像素点的颜色是由“红、绿、蓝”(RGB)三种颜色组合而成。所以,在我们的肉眼看来,一张彩色图像是一个二维平面。但实际上,它是由一张红色图片、一张绿色图片、一张蓝色图片叠加而成的。仿照前面对一张灰度图像用二阶张量来表示,一张彩色就可以用一个三阶张量来表示。

以上只是这个转换过程在我们大脑中的想象,实际通过代码来实现这个过程我们需要借助Pytorch中的torchvision库来实现。

三、实战环节:

1 数据预处理:

当我们进行数据预处理时,归一化是一种常见的操作。它的目的是将数据调整到一个特定的范围或分布,通常是为了让模型更容易学习并且提高训练的稳定性和效率。

归一化的过程就好像把数据放进一个标准的框架中,使得它们的分布更加均匀、方便处理。最常见的归一化方法之一是将数据缩放到 0 到 1 的范围内。举个例子,如果你有一组身高数据,其中最小值是 150 厘米,最大值是 190 厘米,那么归一化之后,150 厘米的数据会变成 0,190 厘米的数据会变成 1,其他的数据会按比例变化到 0 和 1 之间。

而对于图像数据,我们归一化处理的其实是每一个像素点。例如,如果你有一组图像数据,像素值范围在 0 到 255 之间,那么归一化可以将像素值除以 255,将其缩放到 0 到 1 的范围内。

除了将数据缩放到 0 到 1 的范围外,还有其他一些归一化方法,如将数据缩放到均值为 0、标准差为 1 的正态分布中。不同的归一化方法适用于不同的情况,但它们的共同目标都是使数据更易于处理和学习。

小结:

1 归一化的方法:

(1)将数据按比例缩放到0~1之间;

(2)将数据缩放为均值为0,标准差为1的正态分布中。

2 图片数据的归一化是对其每一个像素点做归一化。

代码实现:

import torchvision.transforms as transforms
transform = transforms.Compose([transforms.ToTensor(),  # 将图像转换为张量transforms.Normalize((0,), (1,))  # 归一化,均值为0,标准差为1
])

2 数据加载

2.1 Dataset:

“torchvision.datasets”是 PyTorch 中用于加载常见视觉数据集的模块。

通常情况下,我们需要自定义一个Dataset类来加载我们自己的数据,该类需要实现__len__和__getitem__方法,分别用于返回数据集的长度和访问数据集中的元素。

但Pytorch中提供了torchvision.datasets.MNIST 类,它已经实现了 Dataset 接口,可以直接用于加载 MNIST 数据集。常用的数据集包括 MNIST、CIFAR-10、CIFAR-100、ImageNet 等。

接下来,我们以Minist数据集为例,对其数据进行加载与预处理:

MINIST

Size: 28×28 灰度手写数字图像
Num: 训练集 60000 和 测试集 10000,一共70000张图片
Classes: 0,1,2,3,4,5,6,7,8,9

代码实现:

import torch
from torch.utils.data import Dataset, DataLoader
# 加载训练集和测试集
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
  1. root='./data': 这是指定存储数据集的根目录。在这个例子中,MNIST 数据集将被下载并存储在名为 data 的文件夹中。

  2. train=False: 这是一个布尔值参数,用于指示加载的是否是训练集。在这里,由于我们加载的是测试集,因此将其设为 False

  3. download=True: 这也是一个布尔值参数,用于指示是否下载数据集。如果数据集不存在,则将其设置为 True 将自动下载数据集。一旦下载完成,就会被保存在指定的 root 目录中。

  4. transform=transform: 这是数据转换的参数,用于将加载的图像数据转换为模型可接受的张量格式,并进行归一化处理。在这里,你将之前定义的 transform 应用于加载的测试集数据。

Dataset类负责从数据源中加载数据,并对数据进行预处理(如归一化、数据增强等),但它并不负责将数据组织成批次或进行随机化

2.2 Dataloader:

Dataset负责表示数据集和进行数据预处理,而DataLoader负责将数据组织成批次并加载到模型中进行训练。它接收一个“Dataset”对象作为参数,并根据指定的批量大小、是否打乱数据等参数,在实际使用中,通常会将自定义的Dataset对象传递给DataLoader进行数据加载和批处理(用上文提到的类的继承操作)。

代码实现:

import torch
from torch.utils.data import Dataset, DataLoader
# 创建数据加载器
batch_size = 4
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True,num_workers=0)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=0)
  1. batch_size: 这是指定每个批次(batch)包含的样本数量。在这个例子中,将每个批次设置为包含 4 个样本。

  2. shuffle=False: 这是一个布尔值参数,用于指示是否对数据进行洗牌操作。在测试集中,通常不需要对数据进行洗牌,因此将其设置为 False

  3. num_workers=0: 这是用于数据加载的子进程数量。设置为 0 表示所有数据加载操作都在主进程中进行,没有额外的子进程。在一般情况下,将其设置为大于 0 的值可以加速数据加载,但在某些环境中可能会出现问题,因此这里将其设置为 0,表示只使用主进程进行数据加载操作。

3 构建神经网络模型:

# 构建神经网络模型
class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.flatten = nn.Flatten()self.feature_extraction = nn.Sequential(nn.Linear(28*28, 16*16),nn.ReLU(),nn.Linear(16*16, 8*8),nn.ReLU(),nn.Linear(8*8, 10),nn.ReLU())# 下面定义x的传播路线def forward(self, x):x = self.flatten(x)output = self.feature_extraction(x)return output

在神经网络的构建代码中,注意到输入是28×28 的灰度手写数字图像,输出是0~9的十个数字,经过了三层全连接层来一步步提取特征。

在前行传播代码中,( x ) 是一个形参,表示输入数据。当模型进行训练或推理时,需要将实际的数据传递给模型,这个数据就会被赋值给 ( x )。

4 损失函数与优化器:

# 定义损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)

上例中使用交叉熵损失函数和 SGD 优化器,都是一些固定的格式。不过要注意一 点就是这个学习率,lr=1e-3 是学习率的设置,控制着每次参数更新的步长大小。 学习率是优化算法中一个重要的超参数,它决定了模型在参数空间中搜索的速度 和精度。 例如,lr=1e-3 表示学习率为 0.001,即每次参数更新时,参数值会按照梯度 的方向移动一个较小的步长。

5 定义训练函数与测试函数:

# 定义训练函数
def train(train_dataloader1, model1, loss_fn1, optimizer1):for batch, (X, y) in enumerate(train_dataloader1):X, y = X.to(device), y.to(device)pred = model1(X)loss = loss_fn1(pred, y)optimizer1.zero_grad()loss.backward()optimizer1.step()if batch % 100 == 0:size = len(train_dataloader1)loss, current = loss.item(), batch * len(X)print(f"loss: {loss:.7f} [{current:>5d}/{size:>5d}]")# 定义测试函数
def test(test_dataloader2, model2):size = len(test_dataloader2.dataset)model2.eval()test_loss, correct = 0, 0with torch.no_grad():for X, y in test_dataloader2:X, y = X.to(device), y.to(device)pred = model(X)test_loss += loss_fn(pred, y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= sizecorrect /= sizeprint(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")

6 主程序:

# 主程序
epochs = 5
for t in range(epochs):print(f"Epoch {t+1}\n")train(train_dataloader, model, loss_fn, optimizer)test(test_dataloader, model)

6 模型的保存与评估:

# 保存模型
torch.save(model.state_dict(), "./model")
print("模型保存在model文件夹内")# 加载模型
model = CNN()
model.load_state_dict(torch.load("./model"))
model.eval()  # 将模型设置为评估模式

当调用model.eval()时,就像正在告诉模型:“现在我们要用你来做测试或者预测了,所以请你把自己整理好,别再做什么让结果不确定的事情了。”就好像是在考试前整理心情一样,你希望自己在考试时保持冷静和准确,不要因为紧张或者分心而出错。

四、完整代码:

# 导入库
import torch
import torchvision
# 名称简化
import torch.nn as nn
import torchvision.transforms as transforms
from torch.utils.data import DataLoader# 下载手写数字数据库
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_dataloader = DataLoader(train_dataset, batch_size=4, shuffle=True, num_workers=0)test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True, transform=transform)
test_dataloader = DataLoader(test_dataset, batch_size=4, shuffle=False, num_workers=0)# 构建神经网络模型
class CNN(nn.Module):def __init__(self):super(CNN, self).__init__()self.flatten = nn.Flatten()self.feature_extraction = nn.Sequential(nn.Linear(28*28, 16*16),nn.ReLU(),nn.Linear(16*16, 8*8),nn.ReLU(),nn.Linear(8*8, 10),nn.ReLU())# 下面定义x的传播路线def forward(self, x):x = self.flatten(x)output = self.feature_extraction(x)return outputdevice = torch.device('cuda')  # 将device指定为GPU
model = CNN().to(device)  # 将模型移动到GPU上
print(model)# 定义损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)# 定义训练函数
def train(train_dataloader1, model1, loss_fn1, optimizer1):for batch, (X, y) in enumerate(train_dataloader1):X, y = X.to(device), y.to(device)pred = model1(X)loss = loss_fn1(pred, y)optimizer1.zero_grad()loss.backward()optimizer1.step()if batch % 100 == 0:size = len(train_dataloader1)loss, current = loss.item(), batch * len(X)print(f"loss: {loss:.7f} [{current:>5d}/{size:>5d}]")# 定义测试函数
def test(test_dataloader2, model2):size = len(test_dataloader2.dataset)model2.eval()test_loss, correct = 0, 0with torch.no_grad():for X, y in test_dataloader2:X, y = X.to(device), y.to(device)pred = model(X)test_loss += loss_fn(pred, y).item()correct += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss /= sizecorrect /= sizeprint(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")# 主程序
epochs = 5
for t in range(epochs):print(f"Epoch {t+1}\n")train(train_dataloader, model, loss_fn, optimizer)test(test_dataloader, model)# 保存和恢复网络权值
torch.save(model.state_dict(), "./model")
print("模型保存在model文件夹内")# 加载模型
model = CNN()
model.load_state_dict(torch.load("./model"))
model.eval()  # 将模型设置为评估模式

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/3069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VS调试、debug和release、栈区底层简单介绍、const 修饰指针变量介绍

文章目录 前言一、调试二、debug和release三、调试需要多用,多熟悉四、栈区底层简单介绍五、优秀的代码:常见的coding技巧: 六、const 修饰指针变量1. const 出现在 * 左边2. const 出现在 * 右边 七、strcpy函数的仿写1.版本12. 版本23. 版本34. 版本4 …

Andorid进程间通信之 UNIX SOCKET

1,什么是UNIX SOCKET UNIX SOCKET,域套接字,UNIX SOCKET可用于同一台设备进程间通信,它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序列号应答等,只需要将数据从一个进程复制到另一个进程&…

高精度加法及乘法

目录 字符串的高精度加法 为什么需要高精度加法? 怎么进行高精度加法? 链表的高精度加法 翻转链表(带虚拟头节点) 字符串的高精度乘法 字符串的高精度加法 大数加法_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.co…

linux下查看前10最耗内存的进程

lunux查找占用内存前10的进程 1、使用Top命令查询进程 输入 top 命令,然后按下大写M按照内存MEM排序,按下大写P按照CPU排序。 2、查询占用CPU最高的前10个进程 ps aux|head -1;ps aux|grep -v PID|sort -rn -k 3|head 3、查询占用内存最大的前10个进程…

2024年度西安市创新联合体备案申报条件时间要求须知

一、申报条件 组建市级创新联合体需具备牵头单位、成员单位、组建协议、首席科学家等四个条件。 (一)牵头单位 1.牵头单位应为在西安市注册登记的省市产业链龙头骨干企业,重点支持市级重点产业链“链主”企业; 2.牵头单位一般为1家。 (二)成员单位 1.成员单位…

excel 按照姓名日期年月分组求和

excel 需要按照 姓名 日期的年份进行金额求和统计,采用sumifs 进行统计 注意:sumifs 不支持 合并列拆分计算,合并列只会计算一个值 表格数据大概如下:(sheet) ABC姓名日期金额A2023/01/01500A2023/01/151500B2023/01/01200B202…

python 调试 c++源码

1. gdb常用调试命令概览和说明 2. 编译c库设置Debug模式 cmake设置debug 在CMake中设置debug模式通常意味着启用调试信息和优化。以下是一个简单的CMakeLists.txt文件示例,展示了如何设置项目以便在Debug模式下构建: cmake_minimum_required(VERSION 3…

cad中快速计算多个矩形面积的方法

1、输入命令reg,选中矩形创建面域 2、输入命令uni,选中刚刚创建的面域,组合成一个面域 3、输入命令:LI ,选中面域,即可查看面积和周长 需注意的一点,开始创建的矩形或者多段线要在一个面内,就是…

[沉浸式翻译]最好的网页翻译工具

沉浸式翻译 沉浸式翻译是一种翻译工具,它提供了多种平台的支持,包括桌面端的Edge、Chrome、Firefox、Safari以及移动端的iOS和Android。用户可以在这些平台上安装沉浸式翻译的插件,以便在浏览网页时获得翻译服务。 浏览器的安装教程 详细的…

WEB网站服务器安全漏洞扫描环境搭建及漏洞工具扫描

一、适用环境 1、企业自建有门户网站; 2、使用Struts框架的WEB网站; 3、网站服务器涉及有数据库之类的项目,如:微信登录、手机登录、充值、收费等。 4、使用安卓版、苹果版、电脑版结合的缴费类网站平台。 5、方便但需提高安全性…

排列对称串

Description:很多字串,有些是对称的,有些是不对称的,请将那些对称的字事按从小到大的顺序输出,字事先以长度论大小,如果长度相同,再以ASCI码值为大小标准 Input.输入数据中含有一些字串(1≤串长≤256)。 #…

气膜游泳馆有哪些应用优势呢?-轻空间

气膜游泳馆作为一种利用气膜技术建造的室内体育场馆,具有环保、节能、灵活、美观等特点,适合在各种气候和地形条件下使用。以下是气膜游泳馆具有的应用优势: 1. 全年四季恒温恒湿:气膜游泳馆内部设有智能化的恒温恒湿系统&#xf…

基础环境:wsl2安装Ubuntu22.04 + miniconda

服务器相关信息: Thinkpad p1 gen5 64G 2T 3080ti,自带的有nvidia-smi显卡驱动。使用wsl2安装Ubuntu22.04 miniconda目标:安装gpu版本的PyTorch2.1.2(torch2.1.2/cu117 torchvision0.16.2/cu117) 处理器 12th Gen I…

ubuntu扩展根目录磁盘空间

ubuntu扩展根目录磁盘空间 扩展虚拟机磁盘空间 查看现有磁盘状态 查询现有分区状态,/dev/sda是我们要扩展的磁盘 fdisk -l 开始进行磁盘空间的扩容 parted /dev/sda#扩展3号分区的空间 resizepart 3刷新分区空间 resize2fs /dev/sda3查询扩展结果,…

Linux安装Matlab运行时

一般而言,安装Matlab的linux系统是带桌面版的,如果没带,不在本教程范围内。 一、下载Matlab 下载地址:MATLAB Runtime - MATLAB Compiler - MATLAB 本教程使用R2020b(9.9) 二、linux系统中进行解压 将zip传入linux系统&#xf…

EigenLayer生态全解析:再质押与AVS崛起的序章

基于以太坊网络的再质押协议EigenLayer提出了利用为以太坊网络验证而质押的ETH来与其他协议共享安全性和资本效率,同时为协议参与者提供额外利息。在AVS、再质押、积分系统等概念的推动下,逐渐形成一个庞大的生态系统,从2024年初到现在EigenL…

使用JS代理 实现大对象的功能拆解

序言 在Android开发中,可以通过webView的addJavascriptInterface方法注入一个对象到网页中。但是随着开发的需求越来越多。这个对象身上的方法也越来越多。这个对象对应的java类,体积越来越大,不利于维护。为了在不影响之前代码的基础上。把…

【C++干货基地】深度理解C++中的高效内存管理方式 new delete

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 引入 哈喽各位铁汁们好啊,我是博主鸽芷咕《C干货基地》是由我的襄阳家乡零食基地有感而发,不知道各位的…

Golang基础5-指针、结构体、方法、接口

指针 和c/c类似,但是go语言中指针不能进行偏移和运算,安全指针 &(取地址) *(根据地址取值) nil(空指针) make和new之前对比:make用于初始化slice,map,channel这样的引用类型 而new用于类…

Metasploit Framework(MSF)从入门到实战(一)

MSF的简介 目前最流行、最强大、最具扩展性的渗透测试平台软件 基于Metasploit进行渗透测试和漏洞分析的流程和方法 2003年由HD More发布第一版,2007年用ruby语言重写 架集成了渗透测试标准 (PETS) 思想 一定程度上统一了渗透测试和漏洞研究的工作环…