在gluon接口中,通过Dataset和DataLoader来对数据集进行循环遍历,并返回batch大小的数据,其中Dataset对象用于数据的收集、加载和变换,而DataLoader对象用于返回batch大小的数据。
所有Dataset类中,都有以下四个方法:
getitem(idx)
: 数据加载,用于返回第idx个样本len():
用于返回数据集的样本的数量transform(fn, lazy = True)
: 数据变换,用于返回对每个样本利用fn函数进行数据变换(增广)后的Datasettransform_first(fn, lazy = True)
: 数据变换,用于返回对每个样本的特征利用fn函数进行数据变换(增广)后的Dataset,而不对label进行数据增广import mxnet as mx
mx.random.seed(42) # 固定随机数种子,以便能够复现
X = mx.random.uniform(shape = (10, 3))
y = mx.random.uniform(shape = (10, 1))
dataset = mx.gluon.data.ArrayDataset(X, y) # ArrayDataset不需要从硬盘上加载数据
print(dataset[5]) # 将返回第6个样本的特征和标签,(特征,标签)
模型训练的时候对于数据的格式有一定的要求(channel,height,width)并且要求数据是浮点类型。所以,我们还需要对之前的数据做一些转换。利用transforms可以很方便的实现数据的转换
通过ToTensor方法可以将图片数据转为(channel,height,width)的float类型的数据,通过Normalize方法可以设置数据的均值和标准差,这里将图片的均值设置为0.13,标准差设置为0.30,最后再通过Compose将这些数据转换过程组合成一个链式的处理。
#transforms链式转换数据
transformer = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(0.13,0.30)])
#转换数据
fashion_data = fashion_train_data.transform_first(transformer)
-------------------------------------------------------------------------------------------------
from mxnet.gluon import data as gdata
train= gdata.vision.ImageFolderDataset("path", flag=1)
print train[0] #变换之前的数据
## 数据变换定义
transform_train = gdata.vision.transforms.Compose([ # Compose将这些变换按照顺序连接起来
# 将图片放大成高和宽各为 40 像素的正方形。
gdata.vision.transforms.Resize(40),
# 随机对高和宽各为 40 像素的正方形图片裁剪出面积为原图片面积 0.64 到 1 倍之间的小正方
# 形,再放缩为高和宽各为 32 像素的正方形。
gdata.vision.transforms.RandomResizedCrop(32, scale=(0.64, 1.0),
ratio=(1.0, 1.0)),
# 随机左右翻转图片。
gdata.vision.transforms.RandomFlipLeftRight(),
# 将图片像素值按比例缩小到 0 和 1 之间,并将数据格式从“高 * 宽 * 通道”改为“通道 * 高 * 宽”。
gdata.vision.transforms.ToTensor(),
# 对图片的每个通道做标准化。
gdata.vision.transforms.Normalize([0.4914, 0.4822, 0.4465],
[0.2023, 0.1994, 0.2010])
])
train_ds_transformed = train_ds.transform_first(train)
print train_ds_transformed[0] #变换之后的数据
由于内存的限制,在训练模型的时候通常都是指定一个batch的数据进行迭代训练,所以我们还需要将整个数据转成一个batch迭代器,每次从这个迭代器中取一个batch数据进行训练,gluon提供了一个DataLoader可以实现,而且通过num_workers参数来设置多个线程进行并行处理,但是在windows上测试的时候请将这个参数设置为0,避免线程错误问题
#设置batch的大小
batch_size = 256
#在windows系统上,请将num_workers设置为0,否则会导致线程错误
train_data = gluon.data.DataLoader(fashion_data ,batch_size=batch_size,shuffle=True,num_workers=0)
#每次从tran_data中取出一个batch大小的数据
for data,label in train_data:
print(data.shape,label.shape) #(256, 3, 28, 28) (256,)
使用gluon来构建一个LeNet的网络结构,然后利用MXNet提供的GPU加速来训练模型。
#加载验证数据
fashion_val_data = gluon.data.vision.FashionMNIST(train=False)
val_data = gluon.data.DataLoader(fashion_val_data.transform_first(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.loss.SoftmaxCrossEntropyLoss()
#设置优化算法,使用随机梯度下降sgd算法,学习率设置为0.1
trainer = gluon.Trainer(LeNet.collect_params(),"sgd",{"learning_rate":0.1})
#计算准确率
def acc(output,label):
return (output.argmax(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 = time.time()
for data,label in train_data:
#使用GPU来加载数据加速训练
data_list = gluon.utils.split_and_load(data,gpu_devices)
label_list = gluon.utils.split_and_load(label,gpu_devices)
#前向传播
with autograd.record():
#获取多个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()
trainer.step(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.utils.split_and_load(data,ctx_list=gpu_devices)
label_list = gluon.utils.split_and_load(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),time.time()-epoch_start_time))
#保存模型参数
LeNet.save_parameters("LeNet.params")
在上面介绍了如何来训练模型,并且保存模型文件,我们可以利用已经保存好的模型文件来做很多事情,如直接加载好训练好的模型文件来预测数据、利用模型文件来进行微调等。
from mxnet.gluon import nn
from mxnet.gluon.data.vision import datasets,transforms
from IPython import display
import matplotlib.pyplot 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 = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(0.13,0.30)
])
#获取数据
mnist_valid = datasets.FashionMNIST(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)
preds.append(pred.astype("int32").asscalar())
_,figs = plt.subplots(1,10,figsize=(15,15))
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
display.set_matplotlib_formats("svg")
for f,x,y,pred_y in zip(figs,X,Y,preds):
f.imshow(x.reshape((28,28)).asnumpy())
ax = f.axes
#显示图片的真实标签和预测标签
ax.set_title(text_labels[y]+"\n"+text_labels[pred_y])
#设置字体
ax.title.set_fontsize(14)
#隐藏坐标轴
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()