作为一名算力芯片工程师,平时跟芯片设计打交道比较多。同时希望能对软件/神经网络应用层面有更多的了解,以加强对芯片内部设计需求的理解。此贴记录了自己对神经网络的学习过程。
参考:Mac M1安装Miniconda+支持GPU的TensorFlow和PyTorch_miniconda mac-CSDN博客
神经网络的搭建可以用pytorch,TensorFlow等,推荐在miniconda安装神经网络框架库,方便用conda工具对python版本进行管理。
输入conda --version,输出显示版本号则安装成功。
配置conda源,能让后续的下载有奇效
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/menpo/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
conda config --set show_channel_urls yes
查看配置好的channels
conda config --show channels
配置pip源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
查看源配置情况
pip config list -v
LeNet:Yann LeCun等人在1998年提出的一种早起的卷积神经网络,主要用于数字识别。
AlexNet:Alex Krizhevsky等人在2012年提出的一种具有代表性的卷积神经网络,它在LeNet的基础上进行了改进,使用ReLU作为激活函数,并采用了Dropout来减少过拟合。
ZFNet:牛津大学的Visual Geometry Group于2013年提示的一种基于AlexNet的改进版本,其主要改进在于增加了更多的层数,使得网络深度增加。
VGG:牛津大学Visual Geometry Group于2014年提示的一种卷积神经网络,它通过连续的小滤波器来增加网络深度,从而提高了网络的表达能力。(这里我理解的是,连续的小滤波器可以减少权重参数量)
GoogLeNet:GoodLeNet是google在2014年提出的一种卷积神经网络,采用了Inception模块来增加网络的宽度和深度,提高了网络的表达能力。
ResNet:是微软亚洲研究院的何凯明等人在2015年提出的一种卷积神经网络,它通过引入残差连接来解决深度神经网络训练中的梯度消失问题,从而实现了更深层的网络结构,(卷积神经网络在发展过程中,发现层数变多最后的表现会变差,这是因为引入了劣质层。残差网络的思路是增加一路输出与可能为劣质层的卷积层的结果相加,如果这一层确实表现不好,在训练的过程中网络会让这一层的权重逐渐偏于0,让这一层得不到表达。)
DenseNet:是华南理工大小的Gao Huang等人在2017年提出的一种卷积神经网络,它通过密集连接来优化信息流,从而提高了网络的表达能力。
SENet:谷歌在2017年提出的一种卷积神经网络,它通过引入自适应的通道注意力机制来优化特征表示,从而提高了网络的性能。
SqueezeNet:是由亚利桑大学的Forrest landola等人在2016年提出的一种轻量级的卷积神经网络,他通过使用小型滤波器和火灾模块来减少模型的复杂度,从而实现更快的推理速度。
MobileNet:是由google在2017年提出的一种专为移动设备和嵌入式设备设计的轻量卷积神经网络,它通过使用深度可分离的滤波器来减小模型的复杂度,从而实现更快的推理速度。
参考:【什么是CNN?】浙大大佬教你怎么卷CNN,卷积神经网络CNN从入门到实战,通俗易懂草履虫听了都点头(人工智能、深度学习、机器学习、计算机视觉)_哔哩哔哩_bilibili
库加载、超参数定义、数据加载
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
my_device = "cpu"
if torch.backends.mps.is_available():
my_device = torch.device("mps") #此程序 gpu训练用时:55s, cpu训练用时:157s
#定义超参数
input_size = 28 #图像的总尺寸28*28
num_classes = 10 #标签的种类数
num_epochs = 3 #训练的总循环周期
batch_size = 64 #一个批次的大小,64张图片
#训练集
train_dataset = datasets.MNIST(root="../data",
train=True,
transform=transforms.ToTensor(),
download=False)
#测试集
test_dataset = datasets.MNIST(root="../data",
train=False,
transform=transforms.ToTensor(),
download=False)
# 构建batch数据
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=False)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
神经网络搭建,用的典型的conv+relu+pool结构
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(
in_channels=1, # 灰度图
out_channels=16, # 要得到多少个特征图
kernel_size=5,
stride=1,
padding=2,
), # 输出的特征图为(16,28,28)
nn.ReLU(),
nn.MaxPool2d(kernel_size=2), #输出结果为 (16,14,14)
)
self.conv2 = nn.Sequential(
nn.Conv2d(16,32,5,1,2), #输出 (32,14,14)
nn.ReLU(),
nn.MaxPool2d(2), #输出 (32,7,7)
)
self.out = nn.Linear(32*7*7, 10) #全连接层得到的结果
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) # flatten操作,结果为 (batch_size, 32*7*7)
output = self.out(x)
return output
评估标准函数
#准确率作为评估标准
def accuracy(predictions, labels):
pred = torch.max(predictions.data, 1)[1] # predictions.data.size() -> [64,10] # return the max value in 1-dim
rights = pred.eq(labels.data.view_as(pred)).sum() # view_as()将labels.data的维度变成pred, 统计了和labels.data相等的个数
return rights.cpu(), len(labels) #返回了正确的个数, 总个数
实例化,设置损失函数和优化器
# 实例化
net = CNN().to(mps_device)
#损失函数
criterion = nn.CrossEntropyLoss()
#优化器
optimizer = optim.Adam(net.parameters(), lr=0.001) #定义优化器,普通的随机梯度下降算法
开始训练
#开始训练循环
for epoch in range(num_epochs): #循环次数
#当前epoch的结果保存下来
train_rights = []
for batch_idx, (data, target) in enumerate(train_loader):
net.train()
data = data.to(my_device)
target = target.to(my_device)
output = net(data)
loss = criterion(output, target)
optimizer.zero_grad() #清零
loss.backward()
optimizer.step()
right = accuracy(output, target) #保存训练结果
train_rights.append(right) #保存
if batch_idx % 100 == 0:
net.eval()
val_rights = []
for(data, target) in test_loader:
data = data.to(my_device)
target = target.to(my_device)
output = net(data)
right = accuracy(output, target)
val_rights.append(right)
#准确率计算
train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
print("当前epoch: {} [{}/{} ({:.0f}%)]\t损失: {:.6f}\t训练集准确率: {:.2f}%\t测试集正确率: {:.2f}%".format(
epoch, batch_idx * batch_size, len(train_loader.dataset),
100. * batch_idx / len(train_loader),
loss.data,
100. * train_r[0].numpy() / train_r[1],
100. * val_r[0].numpy() / val_r[1]
))
#####OUTPUT####
#...
#当前epoch: 2 [38400/60000 (64%)] 损失: 0.072756 训练集准确率: 98.73% 测试集正确率: 98.30%
#当前epoch: 2 [44800/60000 (75%)] 损失: 0.029551 训练集准确率: 98.72% 测试集正确率: 98.51%
#当前epoch: 2 [51200/60000 (85%)] 损失: 0.148901 训练集准确率: 98.74% 测试集正确率: 98.84%
#当前epoch: 2 [57600/60000 (96%)] 损失: 0.043769 训练集准确率: 98.76%
torch.max的使用方式
a = torch.randn(5,3)
print(a)
result = torch.max(a,1) # return the max valur in each row
print(result) # contain the values and indices
print(result[1].data.numpy()) # let the result[1].data become numpy type
让图片可视化的方式
def test_data():
print("train_dataloade len", len(test_loader))
for images, labels in test_loader:
print(labels)
print("label len", len(labels))
print("pre result:\t", torch.max(net(images),1)[1])
img = images[7]
img = img.numpy()
img = np.transpose(img, (1, 2, 0)) # C*H*W -> H*W*C
plt.imshow(img)
plt.show()
break
test_data()
从网上下载了一些图片测试此模型,可以看出结果并不是很准确,训练有待加强
import torchvision.transforms as transforms
from PIL import Image
# 定义要加载的图像路径
image_path = "./pic/"
# 创建一个PIL图像对象
def pic2tensor(image_path):
pil_image = Image.open(image_path)
# 从三通道变成灰度图
pil_image = pil_image.convert("L")
# 定义预处理操作,包括调整大小、标准化等
preprocess = transforms.Compose([
transforms.Resize((28, 28)), # 设置目标大小
transforms.ToTensor(), # 转换为张量
])
return preprocess(pil_image)
# 应用预处理操作到图像上
pic0 = pic2tensor(image_path + "pic0.jpg")
pic1 = pic2tensor(image_path + "pic1.jpg")
pic2 = pic2tensor(image_path + "pic2.jpg")
pic3 = pic2tensor(image_path + "pic3.jpg")
pic4 = pic2tensor(image_path + "pic4.jpg")
tensor_image = torch.stack((pic0,pic1,pic2,pic3,pic4))
# 增加batch维度
# tensor_image = tensor_image.unsqueeze(0)
print("转换后的张量形状:", tensor_image.shape)
print("转换后的张量数据类型:", tensor_image.dtype)
tensor_image = tensor_image.to(my_device)
print("pre result:\t", torch.max(net(tensor_image),1)[1])
img = tensor_image.cpu().numpy()
img = np.transpose(img[2], (1, 2, 0)) # C*H*W -> H*W*C
plt.imshow(img)
plt.show()
#####OUTPUT#####
#转换后的张量形状: torch.Size([5, 1, 28, 28])
#转换后的张量数据类型: torch.float32
#pre result: tensor([3, 3, 3, 3, 4], device='mps:0')
保存训练好的参数
torch.save(net.state_dict(), "./cnn_digitR.pth")
使用.pth的方式
model_data = torch.load("./cnn_digitR.pth")
net2 = CNN().to(my_device)
net2.load_state_dict(model_data)
保存为onnx文件,需要提供输入
torch.onnx.export(net, tensor_image, "./cnn_digitR.onnx")