文章目录
本篇博客记录博主搭建神经网络环境的过程。如果对博主的其他文章感兴趣,可以看这篇文章【CSDN文章】博客文章索引
一、环境的搭建
pycharm的安装请看博文:pyCharm专业版破解激活(超详细)。
pycharm中文版,请看博文:Pycharm的汉化方法(pycharm改为中文版)。
有关pycharm如何建立项目的,请看这篇文章:PyCharm创建一个新的项目。
创建完成以后如果你点击了生成初始main函数按钮,那么会自动生成一个hi pycharm的文件。如果想再添加py文件并运行则需要将新建的py文件添加到项目中来,教程请看:Pycharm如何运行.py文件。
GPU加速计算
机器学习或者是深度学习涉及到大量的参数、矩阵运算,除了利用CPU计算以外,最常见的就是用GPU进行加速运算。
GPU就是图形处理器,显卡(显示核心),目前GPU的产商主要有NVIDIA英伟达和ATI(目前是AMD公司,06年被AMD收购)。GPU加速计算方面,Nvidia是先发优势;AMD起步较晚,GPU加速计算方面,Nvidia是先发优势;AMD起步较晚。因此,如果用购买显卡用作人工智能学习或者研究,务必购买NVIDIA卡。GPU主要性能可以从天梯图中观察得到,不同机构的天梯图计算方法也不同。显卡的价格较贵,如果学生负担不起可以使用GPU云服务器作为代替,例如autoDL,使用学生认证会更便宜。
以下环境的安装全部参考自:深度学习环境搭建手把手教程(Anaconda+PyTorch)——看这一篇就够了
注意我们尽量安装高版本的python,CUDA,驱动,因为有一些包需要高版本的Python解释器,CUDA,驱动支持,这三个是向下兼容的。但是考虑到学习的代码都是近1-5年的,也不适合安装最新的软件。所以在学习不同的代码时,需要的环境也不同,要根据具体的论文需要配置所需环境。因此Anaconda就是来帮助我们管理不同的环境配置的软件。
Anaconda安装
Anaconda是一个集成的Python包以及环境,包含依赖项等,可以用来管理包,安装、运行应用,其包含了conda、Python等180多个科学包及其依赖项,Anaconda + Jupyter 基本上已经是大部分机器学习/数据分析等开发者标配的开发环境。其中,Jupyter是python的代码编辑器。针对不同版本的Python需求,Anaconda可以帮助建立管理多个环境,以避免不同环境的相互影响,提供一个更加快速和简单的方式来运行机器学习的相关代码。简单来讲,Anaconda是一个可以帮你管理不同版本Python包的软件。
解释器:图中的Python.exe就是解释器,是将Python这种高级语言转换成计算机可理解的可执行程序。
CUDA安装
CUDA:CUDA是NVIDIA推出的运算平台。 它是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。 它包含了CUDA指令集架构(ISA)以及GPU内部的并行计算引擎。深度神经网络库 (cuDNN) 是一个GPU加速的深度神经网络基元库。cuDNN可大幅度优化标准例程(例如前向和后向卷积、池化、归一化和激活层)的实施。总的来说,二者的作用是促使GPU加速。
NVIDIA系列显卡做深度学习,需要在宿主机安装显卡驱动(driver),需要在docker中安装英伟达的科学计算库(cuda),在python中安装深度学习库。博主用的是pytorch,pytorch依赖cuda,cuda依赖driver,driver依赖显卡,于是就会有一些版本依赖问题。简单来讲,我们先安装显卡驱动driver,然后再安装兼容的cuda,接着安装pytorch。
pytorch、显卡、显卡驱动、cuda版本是如何对应的
cuda和显卡驱动基本都是向下兼容的,意味着我安装最新的cuda和显卡驱动,基本上可以支持以前的几乎所有显卡。当然了,代价是需要更新我们的python深度学习库比如pytorch,tensorflow等指定cuda的版本,否则可能会出现一些兼容问题。
驱动安装:GeForce® 驱动程序。
因此,我们先查询pytorch支持的cuda版本,pytorch官网。图中博主查询了最新的pytorch支持的cuda版本,然后在CUDA Toolkit Archive下载了cuda12.1(根据自己的系统选择下载,博主是windows11本地下载)。
pytorch安装
机器学习框架是一个库、接口或工具,它是在不需要开发人员深入研究幕后使用的算法的细节的情况下完成的。它提供了一种简单明了的方法,通过使用预构建和优化组件库来定义机器学习模型。
实际上相当于一个Python的机器学习库,基于Torch,可以理解为一个包含很多机器学习函数的库文件。
比较著名的架构有TensorFlow、Keras、PyTorch等等。截止至2024年,pytorch使用的人数最多,博主使用的也是pytorch。
博主安装时碰见了function错误的问题,如下图所示,是因为输入的指令不正确导致的。解决安装pytorch出现的问题。
学术名词解释
epoch:代表所有样本训练的次数。训练完所有的样本叫做一个epoch。
batch/batch_size/iteration:完整的训练所有样本所需时间太长,就把样本划分成一个个batch,一个batch大小为batch_size,训练一个Batch就是一次Iteration。
learning rate:模型的学习速度,损失函数的收敛速度。
Baselines:参照物/基线,假如论文是根据另外一篇论文改进的,那么论文就是你的论文的baselines。
二、MNIST手写字识别程序
编写下面代码之前,建议先学习基于Python的Pytorch代码:60分钟快速入门 PyTorch。
下面我们完成手写字识别的简单程序。手写字识别算是深度学习界的helloworld程序。
以下参考自用PyTorch实现MNIST手写数字识别(非常详细)。
在本文中,我们将在PyTorch中构建一个简单的卷积神经网络,并使用MNIST数据集训练它识别手写数字。在MNIST数据集上训练分类器可以看作是图像识别的“hello world”。
MNIST包含70,000张手写数字图像: 60,000张用于培训,10,000张用于测试。图像是灰度的,28x28像素的,并且居中的,以减少预处理和加快运行。
设置环境包
环境代码如下:
##################################### 环境包
import torch
import torchvision # 包含了常用数据集,MNIST也在里面
from torch.utils.data import DataLoader
import torch.nn as nn # nn是neural的简称,帮助程序员写神经网络代码的包
import torch.nn.functional as F # 激活函数或者常见的函数
import torch.optim as optim # 优化器
import matplotlib.pyplot as plt # 绘图工具包
from tqdm import tqdm # 进度条包
import time # 计时器包
torch.nn库的介绍如下:torch.nn库五大基本功能。
基本参数配置
基本参数配置如下如下:
##################################### 参数设置
n_epochs = 8 # 完整训练次数 8
batch_size_train = 64 # 训练集batch_size大小
batch_size_test = 1000 # 测试集batch_size大小
learning_rate = 0.01 # 学习率
momentum = 0.5 # 优化器的参数
log_interval = 10 # 在屏幕上输出的batch间隔
random_seed = 1 # 随机数种子
torch.manual_seed(random_seed) # 根据CPU生成一个固定的随机数,方便实验复现
导入数据
测试集和训练集数据导入如下:
###################################################### 训练集和测试集数据导入
# 一共三个输入参数,data, batch_size, shuffle
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size_train, shuffle=True)
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size_test, shuffle=True)
设置网络架构
设置网络参数如下,深度学习中Dropout原理解析。
############################################ 网络参数设置
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__() # Net网络继承nn.Module
self.conv1 = nn.Conv2d(1, 10, kernel_size=5) # 两个2d卷积层
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d() # 正则化层
self.fc1 = nn.Linear(320, 50) # 两个全连接层
self.fc2 = nn.Linear(50, 10)
def forward(self, x): # 前向传播函数
# 第一层包含:卷积层,最大池化层,relu激活函数
x = F.relu(F.max_pool2d(self.conv1(x), 2))
# 第二层包含:卷积层,正则化层,最大池化层,激活函数
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320) # view相当于一个reshape函数
# 第三层包含:全连接层,激活函数
x = F.relu(self.fc1(x))
# 第四层包含:dropout层
x = F.dropout(x, training=self.training)
# 第五层包含:全连接层
x = self.fc2(x)
return F.log_softmax(x, dim=1)
训练函数
训练函数如下,与原文不同之处在于博主加入了GPU训练,然后加入tqdm取代之前的print函数。GPU加速训练需要同时把网络和数据都放在GPU上,否则会报错。
####################################### 训练
def train(epoch):
network.train() # 告诉网络要训练了
loop = tqdm(enumerate(train_loader), total = len(train_loader))
running_loss = 0.0
right = 0
for batch_idx, (data, target) in loop: # data是要训练的数据,target是数据的标签
# 将训练数据移到GPU上
data = data.cuda()
target = target.cuda()
output = network(data) # 输出
loss = F.nll_loss(output, target) # 通过损失函数计算损失
optimizer.zero_grad() # 设置初始梯度为0
loss.backward() # 根据损失反向传播
optimizer.step()
running_loss += loss.item()
pred = output.data.max(1, keepdim=True)[1]
right += pred.eq(target.data.view_as(pred)).sum() # 累加识别正确的样本数
loop.set_description(f'Epoch [{epoch}/{n_epochs}]')
loop.set_postfix(loss=running_loss / (batch_idx + 1), acc=float(right) / float(batch_size_train * batch_idx + len(data)))
# 如果设置log_interval = 10,所以每隔10个batch会输出,
# 而batch_size_train = 64, 所以每隔640个数据输出一次。
if batch_idx % log_interval == 0:
train_losses.append(loss.item())
train_counter.append((batch_idx * 64) + ((epoch - 1) * len(train_loader.dataset)))
torch.save(network.state_dict(), './model/model.pth') # 保存网络权重参数
torch.save(optimizer.state_dict(), './model/optimizer.pth') # 保存优化器的值
测试函数
测试代码如下:
######################################### 测试
def test():
network.eval() # 告诉网络要测试了,固定住网络权重
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data = data.cpu() # 不加会报错,显示网络和数据一个在GPU上一个在CPU上
output = network(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target.data.view_as(pred)).sum()
test_loss /= len(test_loader.dataset)
test_losses.append(test_loss)
print('Test set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
训练+测试+绘制图像
开始训练+测试+绘制图像:
################################## 训练一次看看结果
print('训练前测试集的结果:\n\n')
test() # 不加这个,后面画图就会报错:x and y must be the same size
############################## 将网络移动到GPU上
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 有没有GPU
network = network.to(device)
################################### 开始训练
start = time.time()
for epoch in range(1, n_epochs + 1): # 范围为[1, n_epochs + 1)
train(epoch)
end = time.time()
print('Time cost:', end - start, 's') # 训练耗时
print('训练后测试集的结果:\n')
network = network.cpu()
test()
#################################### 网络的训练和测试集损失函数结果
fig = plt.figure()
plt.plot(train_counter, train_losses, color='blue')
plt.legend(['Train Loss', 'Test Loss'], loc='upper right')
plt.xlabel('number of training examples seen')
plt.ylabel('negative log likelihood loss')
plt.savefig('./image/secondResult.svg')
#################################### end
绘制神经网络框图
出现的警告和错误
博主自己在做的过程中出现的错误和警告如下,分别查询了博客文章成功解决,其中画图警告使用解决方法一即可;OMP错误使用解决方法二、三都可行,建议使用解决方法二,一劳永逸。
Pycharm中画图警告:MatplotlibDeprecationWarning
总结该问题解决方案:OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized
OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized
MatplotlibDeprecationWarning: Support for FigureCanvases without a required_interactive_framework attribute was deprecated in Matplotlib 3.6 and will be removed two minor releases later.
实验结果
我们实现了识别准确率为98%的模型。
CPU训练:
GPU训练
手写字识别完整代码
# MNIST手写字识别程序
# By 2024.3.6 Mr.Huang
##################################### 环境包
import torch
import torchvision # 包含了常用数据集,MNIST也在里面
from torch.utils.data import DataLoader
import torch.nn as nn # nn是neural的简称,帮助程序员写神经网络代码的包
import torch.nn.functional as F # 激活函数或者常见的函数
import torch.optim as optim # 优化器
import matplotlib.pyplot as plt # 绘图工具包
from tqdm import tqdm # 进度条包
import time # 计时器包
##################################### 参数设置
n_epochs = 8 # 完整训练次数 8
batch_size_train = 256 # 训练集batch_size大小
batch_size_test = 1000 # 测试集batch_size大小
learning_rate = 0.01 # 学习率
momentum = 0.5 # 优化器的参数
log_interval = 10 # 在屏幕上输出的batch间隔
random_seed = 1 # 随机数种子
torch.manual_seed(random_seed) # 根据CPU生成一个固定的随机数,方便实验复现
###################################################### 训练集和测试集数据导入
# 一共三个输入参数,data, batch_size, shuffle
train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=True, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size_train, shuffle=True)
test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST('./data/', train=False, download=True,
transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize(
(0.1307,), (0.3081,))
])),
batch_size=batch_size_test, shuffle=True)
############################################ 网络参数设置
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__() # Net网络继承nn.Module
self.conv1 = nn.Conv2d(1, 10, kernel_size=5) # 两个2d卷积层
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d() # 正则化层
self.fc1 = nn.Linear(320, 50) # 两个全连接层
self.fc2 = nn.Linear(50, 10)
def forward(self, x): # 前向传播函数
# 第一层包含:卷积层,最大池化层,relu激活函数
x = F.relu(F.max_pool2d(self.conv1(x), 2))
# 第二层包含:卷积层,正则化层,最大池化层,激活函数
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320) # view相当于一个reshape函数
# 第三层包含:全连接层,激活函数
x = F.relu(self.fc1(x))
# 第四层包含:dropout层 防止过拟合
x = F.dropout(x, training=self.training)
# 第五层包含:全连接层
x = self.fc2(x)
return F.log_softmax(x, dim=1)
###############################################初始化网络,优化器
network = Net()
optimizer = optim.SGD(network.parameters(), lr=learning_rate, momentum=momentum) # 优化器设置
train_losses = []
train_counter = [] # 训练集计数器,用来打印输出/显示
test_losses = []
test_counter = [i * len(train_loader.dataset) for i in range(n_epochs + 1)]
####################################### 训练
def train(epoch):
network.train() # 告诉网络要训练了
loop = tqdm(enumerate(train_loader), total = len(train_loader))
running_loss = 0.0
right = 0
for batch_idx, (data, target) in loop: # data是要训练的数据,target是数据的标签
# 将训练数据移到GPU上
data = data.cuda()
target = target.cuda()
output = network(data) # 输出
loss = F.nll_loss(output, target) # 通过损失函数计算损失
optimizer.zero_grad() # 设置初始梯度为0
loss.backward() # 根据损失反向传播
optimizer.step()
running_loss += loss.item()
pred = output.data.max(1, keepdim=True)[1]
right += pred.eq(target.data.view_as(pred)).sum() # 累加识别正确的样本数
loop.set_description(f'Epoch [{epoch}/{n_epochs}]')
loop.set_postfix(loss=running_loss / (batch_idx + 1), acc=float(right) / float(batch_size_train * batch_idx + len(data)))
# 如果设置log_interval = 10,所以每隔10个batch会输出,
# 而batch_size_train = 64, 所以每隔640个数据输出一次。
if batch_idx % log_interval == 0:
train_losses.append(loss.item())
train_counter.append((batch_idx * 64) + ((epoch - 1) * len(train_loader.dataset)))
torch.save(network.state_dict(), './model/model.pth') # 保存网络权重参数
torch.save(optimizer.state_dict(), './model/optimizer.pth') # 保存优化器的值
######################################### 测试
def test():
network.eval() # 告诉网络要测试了,固定住网络权重
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data = data.cpu() # 不加会报错,显示网络和数据一个在GPU上一个在CPU上
output = network(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target.data.view_as(pred)).sum()
test_loss /= len(test_loader.dataset)
test_losses.append(test_loss)
print('Test set: Avg. loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
################################## 训练一次看看结果
print('训练前测试集的结果:\n\n')
test() # 不加这个,后面画图就会报错:x and y must be the same size
############################## 将网络移动到GPU上
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 有没有GPU
network = network.to(device)
################################### 开始训练
start = time.time()
for epoch in range(1, n_epochs + 1): # 范围为[1, n_epochs + 1)
train(epoch)
end = time.time()
print('Time cost:', end - start, 's') # 训练耗时
print('训练后测试集的结果:\n')
network = network.cpu()
test()
#################################### 网络的训练和测试集损失函数结果
fig = plt.figure()
plt.plot(train_counter, train_losses, color='blue')
plt.legend(['Train Loss', 'Test Loss'], loc='upper right')
plt.xlabel('number of training examples seen')
plt.ylabel('negative log likelihood loss')
plt.savefig('./image/secondResult.svg')
#################################### end
未完待续
end