前言
在前面几篇文章中详细介绍了MXNet的一些特点以及入门基础知识,本篇文章主要介绍如何使用MXNet来训练模型、加载模型进行预测、预训练模型以及MXNet中gpu使用的相关知识。
在介绍训练模型之前,先介绍MXNet如何使用GPU,因为后面训练模型需要利用GPU来加速训练。
MXNet的GPU使用
在训练神经网络的时候,为了获取一个效果比较好的模型通常都需要上万级,甚至上亿级的数据量,所以这需要强大的计算力,通过使用GPU能极大的提升计算机的运算速度,减少训练时间。所以深度学习框架都会支持GPU,不过目前MXNet只支持NVIDA GPU,还不支持AMD GPU和Intel GPU。如果想要安装MXNet GPU版本则需要先安装Cuda和Cudnn,具体安装方法在这里我就不详细介绍了
- 安装gpu的mxnet
windows系统请打开cmd输入nvcc --version可以看到如下信息
通过上图可以看出Cuda的版本是9.0的,所以在安装MXNet GPU版本请用pip install mxnet-cu90进行安装
如果是Ubuntu系统通过nvidia-smi命令查看Cuda的版本,然后再通过pip命令安装即可
注意:安装的MXNet的版本与cuda的版本不相符合,会导致报cuda* lib not found的错误
- 查看电脑有几个GPU
可以通过mxnet的test_utils包进行查看,代码如下
import mxnet as mx #查看GPU的个数 mx.() #输出 #range(0, 0)通过结果可以发现,我的电脑上只有一个GPU,除此之外,还可以通过mxnet在创建NDArray时,调用不同的GPU,如果这个GPU不存在就会抛异常的特性来查看GPU的数量
for i in range(10): try: #通过ctx参数来选择GPU mx.nd.zeros((1,),ctx=mx.gpu(i)) except: print("GPU num:%d"%i) break #输出结果 GPU num:1- 在GPU上创建NDArray
通过ctx参数可以指定数组创建在哪个GPU上,默认是GPU0,没有指定ctx参数,默认是使用CPU
#使用GPU创建NDArray mx.nd.ones((3,4),ctx=mx.gpu()) """ [[1. 1. 1. 1.] [1. 1. 1. 1.] [1. 1. 1. 1.]] <NDArray 3x4 @gpu(0)> """- GPU的通信
使用MXNet也很容易的再多个GPU之间进行通信,将某个数据拷贝到另一个GPU上,只需要执行以下代码即可,其中x表示的是NDArray
注意:电脑没有1个以上GPU执行以下代码会报错
x.copyto(gpu(1))可视化分析数据
在之前的文章中有介绍过,如何使用MXNet的gluon模块来创建一个神经网络,这里我们将详细介绍怎么样来训练网络,将会介绍如何利用mxnet.init来初始化权重参数,利用datasets和tansforms来加载和转换数据,通过matplotlib来可视化数据,通过time来统计计算时间
- 导包
- 加载数据
通过gluon.da.datasets来自动的下载数据集,本次主要是构建一个分类网络,来实现FasionMNIST数据集的分类,该数据集主要包含了一些服装的类别,相对于MNIST数据集来说,分类难度稍微要大一些,接下来我们先来下载数据集。训练集一共包含了60000张图片,每张图片的大小为28*28,一共有十种类别
#下载FashionMNIST数据集 fashionMNIST_trian = da(train=True) #获取图标和对应的标签 images,labels = fashionMNIST_trian[:] #获取数据的大小 #一共有60000张图片,图片大小是28*28 print(images.shape,labels.shape) #(60000, 28, 28, 1) (60000,) #输出类别标签,类别标签是由0-9组成的类别,分别对应不同的服装类型 print(labels[0]) #2- 显示数据
从训练集中挑选出25张图片来进行可视化显示,便于更好的了解数据
def plot_show(): #设置每个类标的标签 text_labels = ["t-shirt","trouser","pullover","dress","coat", "sandal","shirt","sneaker","bag","ankle boot"] #从训练集中选25个图像可视化 part_images,part_labels = images[:25],labels[:25] di("svg") _,figs = (5,5,figsize=(15,15)) for row in range(5): for col in range(5): f = figs[row][col] #将3D图片数据转为2D便于可视化 f.imshow(part_images[row*5+col].reshape((28,28)) .asnumpy()) ax = f.axes #设置标题 ax.set_title(text_labels[int(part_labels[row*5+col])]) #设置字体大小 ax.(14) #隐藏坐标轴 ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ()训练模型
在之前的文章中有介绍过使用gluon来构建模型,这里我们将详细介绍如何利用gluo来训练、加载、预训练模型。
- 转换数据
在前面可视化数据的时候介绍了通过gluon.vi来加载数据,在使用gluon构建模型的时候需要使用到这些数据,模型训练的时候对于数据的格式有一定的要求(channel,height,width)并且要求数据是浮点类型。所以,我们还需要对之前的数据做一些转换。利用transforms可以很方便的实现数据的转换
#transforms链式转换数据 transformer = ([ (), )]) #转换数据 fashion_data = (transformer)通过ToTensor方法可以将图片数据转为(channel,height,width)的float类型的数据,通过Normalize方法可以设置数据的均值和标准差,这里将图片的均值设置为0.13,标准差设置为0.31,最后再通过Compose将这些数据转换过程组合成一个链式的处理。
由于内存的限制,在训练模型的时候通常都是指定一个batch的数据进行迭代训练,所以我们还需要将整个数据转成一个batch迭代器,每次从这个迭代器中取一个batch数据进行训练,gluon提供了一个DataLoader可以实现,而且通过num_workers参数来设置多个线程进行并行处理,但是在windows上测试的时候请将这个参数设置为0,避免线程错误问题
#设置batch的大小 batch_size = 256 #在windows系统上,请将num_workers设置为0,否则会导致线程错误 train_data = gluon.da(fashion_data ,batch_size=batch_size,shuffle=True,num_workers=0) #每次从tran_data中取出一个batch大小的数据 for data,label in train_data: prin)#(256, 1, 28, 28) (256,) break- 训练模型保存模型文件
使用gluon来构建一个LeNet的网络结构,然后利用MXNet提供的GPU加速来训练模型。MXNet可以很方便就能够支持多个GPU同时加速训练,在使用GPU加速训练的时候,需要特别注意需要将参数和数据都放在GPU上,否则会出现错误。
训练完成之后,保存模型文件的时候,MXNet提供了两种不同的方式,在保存模型文件的时候,可以选择只保存参数或同时保存参数和模型结构,通常参数是保存在.params文件中,模型结构是保存在.json文件中。如果只保存了参数,在加载模型的时候,则需要事先定义好网络结构给网络的参数赋值。如果有.json文件,则不需要定义网络的结构,直接通过.josn文件加载即可。
#加载验证数据 fashion_val_data = gluon.da.FashionMNIST(train=False) val_data = gluon.da(transformer), batch_size=batch_size,num_workers=0) #定义使用的GPU,使用GPU加速训练,如果有多个GPU,可以定义多个 gpu_devices = [mx.gpu(0)] #定义网络结构 LeNet = nn.Sequential() #构建一个LeNet的网络结构 LeNet.add( nn.Conv2D(channels=6,kernel_size=5,activation="relu"), nn.MaxPool2D(pool_size=2,strides=2), nn.Conv2D(channels=16,kernel_size=3,activation="relu"), nn.MaxPool2D(pool_size=2,strides=2), nn.Flatten(), nn.Dense(120,activation="relu"), nn.Dense(84,activation="relu"), nn.Dense(10) ) #初始化神经网络的权重参数,使用GPU来加速训练 LeNet.collect_params().initialize(force_reinit=True,ctx=gpu_devices) #定义softmax损失函数 softmax_cross_entropy = gluon.lo() #设置优化算法,使用随机梯度下降sgd算法,学习率设置为0.1 trainer = gluon.Trainer(),"sgd",{"learning_rate":0.1}) #计算准确率 def acc(output,label): return (axis=1) == label.astype("float32")).mean().asscalar() #设置迭代的轮数 epochs = 10 #训练模型 for epoch in range(epochs): train_loss,train_acc,val_acc = 0,0,0 epoch_start_time = () for data,label in train_data: #使用GPU来加载数据加速训练 data_list = gluon.u(data,gpu_devices) label_list = gluon.u(label,gpu_devices) #前向传播 with au(): #获取多个GPU上的预测结果 pred_Y = [LeNet(x) for x in data_list] #计算多个GPU上预测值的损失 losses = [softmax_cross_entropy(pred_y,Y) for pred_y,Y in zip(pred_Y,label_list)] #反向传播更新参数 for l in losses: l.backward() (batch_size) #计算训练集上的总损失 train_loss += sum([l.sum().asscalar() for l in losses]) #计算训练集上的准确率 train_acc += sum([acc(output_y,y) for output_y,y in zip(pred_Y,label_list)]) for data,label in val_data: data_list = gluon.u(data,ctx_list=gpu_devices) label_list = gluon.u(label,ctx_list=gpu_devices) #计算验证集上的准确率 val_acc += sum(acc(LeNet(val_X),val_Y) for val_X,val_Y in zip(data_list,label_list)) print("epoch %d,loss:%.3f,train acc:%.3f,test acc:%.3f,in %.1f sec"% (epoch+1,train_loss/len(labels),train_acc/len(train_data),val_acc/len(val_data),()-epoch_start_time)) #保存模型参数 LeNet.save_parameters("LeNet.params")- 保存网络结构和参数文件
想要同时将模型参数和模型结构保存为文件,其实也很简单,只需要修改上面几行代码即可,将nn.Sequential修改为nn.HybridSequential,然后再添加调用hybridize(),将最后保存模型文件的代码由save_parameters方法改为export,具体代码如下
import mxnet as mx from mxnet.gluon import nn from mxnet import gluon,nd,autograd,init from mxnet.gluon.da import datasets,transforms from IPython import display import ma as plt import time import numpy as np #下载fashionMNIST数据集 fashion_train_data = da(train=True) #获取图片数据和对应的标签 images,labels = fashion_train_data[:] #transforms链式转换数据 transformer = ([ (), )]) #转换数据 fashion_data = (transformer) #设置batch的大小 batch_size = 256 #在windows系统上,请将num_workers设置为0,否则会导致线程错误 train_data = gluon.da(fashion_data,batch_size=batch_size,shuffle=True,num_workers=0) #加载验证数据 fashion_val_data = gluon.da.FashionMNIST(train=False) val_data = gluon.da(transformer), batch_size=batch_size,num_workers=0) #定义使用的GPU,使用GPU加速训练,如果有多个GPU,可以定义多个 gpu_devices = [mx.gpu(0)] #定义网络结构 LeNet = nn.HybridSequential() #构建一个LeNet的网络结构 LeNet.add( nn.Conv2D(channels=6,kernel_size=5,activation="relu"), nn.MaxPool2D(pool_size=2,strides=2), nn.Conv2D(channels=16,kernel_size=3,activation="relu"), nn.MaxPool2D(pool_size=2,strides=2), nn.Flatten(), nn.Dense(120,activation="relu"), nn.Dense(84,activation="relu"), nn.Dense(10) ) LeNet.hybridize() #初始化神经网络的权重参数,使用GPU来加速训练 LeNet.collect_params().initialize(force_reinit=True,ctx=gpu_devices) #定义softmax损失函数 softmax_cross_entropy = gluon.lo() #设置优化算法,使用随机梯度下降sgd算法,学习率设置为0.1 trainer = gluon.Trainer(),"sgd",{"learning_rate":0.1}) #计算准确率 def acc(output,label): return (axis=1) == label.astype("float32")).mean().asscalar() #设置迭代的轮数 epochs = 10 #训练模型 for epoch in range(epochs): train_loss,train_acc,val_acc = 0,0,0 epoch_start_time = () for data,label in train_data: #使用GPU来加载数据加速训练 data_list = gluon.u(data,gpu_devices) label_list = gluon.u(label,gpu_devices) #前向传播 with au(): #获取多个GPU上的预测结果 pred_Y = [LeNet(x) for x in data_list] #计算多个GPU上预测值的损失 losses = [softmax_cross_entropy(pred_y,Y) for pred_y,Y in zip(pred_Y,label_list)] #反向传播更新参数 for l in losses: l.backward() (batch_size) #计算训练集上的总损失 train_loss += sum([l.sum().asscalar() for l in losses]) #计算训练集上的准确率 train_acc += sum([acc(output_y,y) for output_y,y in zip(pred_Y,label_list)]) for data,label in val_data: data_list = gluon.u(data,ctx_list=gpu_devices) label_list = gluon.u(label,ctx_list=gpu_devices) #计算验证集上的准确率 val_acc += sum(acc(LeNet(val_X),val_Y) for val_X,val_Y in zip(data_list,label_list)) print("epoch %d,loss:%.3f,train acc:%.3f,test acc:%.3f,in %.1f sec"% (epoch+1,train_loss/len(labels),train_acc/len(train_data),val_acc/len(val_data),()-epoch_start_time)) #保存模型参数 LeNet.export("lenet",epoch=1) #加载模型文件 LeNet = gluon.nn.SymbolBlock.imports("lene;,["data"],"lene;)加载预训练模型
在上面介绍了如何来训练模型,并且保存模型文件,我们可以利用已经保存好的模型文件来做很多事情,如直接加载好训练好的模型文件来预测数据、利用模型文件来进行微调等。
from mxnet.gluon import nn from mxnet.gluon.da import datasets,transforms from IPython import display import ma as plt #构建模型 LeNet = nn.Sequential() #注意模型的结果必须与训练时的模型结构一模一样 LeNet.add( nn.Conv2D(channels=6,kernel_size=5,activation="relu"), nn.MaxPool2D(pool_size=2,strides=2), nn.Conv2D(channels=16,kernel_size=3,activation="relu"), nn.MaxPool2D(pool_size=2,strides=2), nn.Flatten(), nn.Dense(units=120,activation="relu"), nn.Dense(84,activation="relu"), nn.Dense(10) ) #加载模型 LeNet.load_parameters("LeNet.params") #构建一个数据转换器 transformer = ([ (), ) ]) #获取数据 mnist_valid = da(train=False) X,Y = mnist_valid[:10] preds = [] for x in X: x = transformer(x).expand_dims(axis=0) #获取预测结果 pred = LeNet(x).argmax(axis=1) ("int32").asscalar()) _,figs = (1,10,figsize=(15,15)) text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot'] di("svg") for f,x,y,pred_y in zip(figs,X,Y,preds): f.imshow((28,28)).asnumpy()) ax = f.axes #显示图片的真实标签和预测标签 ax.set_title(text_labels[y]+"\n"+text_labels[pred_y]) #设置字体 ax.(14) #隐藏坐标轴 ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ()从Gluon model zoo加载预训练模型
Gluon model zoo提供了非常多的预训练模型(AlexNet、DenseNet、Inception、ResNet、MobileNet等),都是基于ImageNet数据训练的,我们可以很方便的去加载这些模型,然后在我们自己的数据上进行微调,以获取一个适合自己数据的模型。
gluon model zoo:
from mxnet.gluon.model_zoo import vision as models from mxnet.gluon.utils import download from mxnet import image,nd import ma as plt #加载一个ResNet v2模型 net = models.resnet50_v2(pretrained=True) #从网络上下载imageNET的标签名称 url = 'http://data.mxnet.io.s3-website-us-west-1.amazonaws.com/models/imagenet/synset.txt' fname = download(url) with open(fname, 'r') as f: text_labels = [' '.join()[1:]) for l in f] #获取网络上的图片 url = '\ Golden_Re\ 365; fname = download(url) x = image.imread(fname) #将图片的短边缩放为256个像素 x = image.resize_short(x, 256) #从图片中心随机裁剪一个224大小的图片 x, _ = image.center_crop(x, (224,224)) #显示图片 # ()) # () #转换数据 def transform(data): data = da((2,0,1)).expand_dims(axis=0) rgb_mean = nd.array([0.485, 0.456, 0.406]).reshape((1,3,1,1)) rgb_std = nd.array([0.229, 0.224, 0.225]).reshape((1,3,1,1)) #设置图片的均值和方差 return ('float32') / 255 - rgb_mean) / rgb_std #获取预测图片的结果 prob = net(transform(x)).softmax() #获取top5的结果 idx = (k=5)[0] for i in idx: i = in()) print('With prob = %.5f, it contains %s' % ( prob[0,i].asscalar(), text_labels[i]))