深度卷积神经网络在CPU上计算的加速
在利用MXNet对AlexNet简化模拟的实现过程中,由于机器没有独显,只能利用CPU进行计算。😦
结果发现,GPU上只用一两分钟跑完的5个epoch,CPU跑了十几分钟连一个epoch还没跑出来。这是代码能干得出来的事?欺负一个CPU算啥!CPU:“ 我太难了!”
原始读取数据函数
#通过Resize将图像高和宽扩大到AlexNet使用的图像高和宽224
def load_data_fashion_mnist(batch_size, resize=None, root=os.path.join('~', '.mxnet', 'datasets', 'fashion-mnist')):
root = os.path.expanduser(root)
transformer = []
if resize:
transformer += [gdata.vision.transforms.Resize(resize)]
transformer += [gdata.vision.transforms.ToTensor()]
transformer = gdata.vision.transforms.Compose(transformer)
mnist_train = gdata.vision.FashionMNIST(root=root, train=True)
mnist_test = gdata.vision.FashionMNIST(root=root, train=False)
num_workers = 0 if sys.platform.startswith('win') else 4
train_iter = gdata.DataLoader(mnist_train.transform_first(transformer), batch_size, shuffle=True, num_workers=num_workers)
test_iter = gdata.DataLoader(mnist_test.transform_first(transformer), batch_size, shuffle=False, num_workers=num_workers)
return train_iter, test_iter
函数优化
但这个函数有一个问题,运行代码的时候,内存占用异常的大。 这个函数将resize的过程放到了DataLoader函数之前,虽然速度可能有一些提升,但是内存占用的代价太大了。这样的话,CPU内存可能就爆掉了。
考虑将resize的步骤放到函数DataLoader里面。调用每一个batch的时候,将当前batch的数据resize。
首先定义一个DataLoader类,与mxnet.gluon.data.DataLoader不同的是,这个类使得读取数据时是读取一个batch后进行一次resize,而不是读取每个样本时就resize。
class DataLoader():
def __init__(self, dataset, batch_size, shuffle, resize):
self.dataset = dataset
self.batch_size = batch_size
self.shuffle = shuffle
self.resize = resize
def __iter__(self):
data = self.dataset[:]
X = data[0]
y = nd.array(data[1])
n = X.shape[0]
if self.shuffle:
idx = np.arange(n)
np.random.shuffle(idx)
X = nd.array(X.asnumpy()[idx])
y = nd.array(y.asnumpy()[idx])
for i in range(n//self.batch_size):
batch_x = X[i*self.batch_size:(i+1)
* self.batch_size].astype('float32')
if self.resize:
new_data = nd.zeros(
(self.batch_size, self.resize, self.resize, batch_x.shape[3]))
for j in range(self.batch_size):
new_data[j] = image.imresize(
batch_x[j], self.resize, self.resize)
batch_x = new_data
yield (nd.transpose(batch_x, (0, 3, 1, 2))/255, y.astype('float32')[i*self.batch_size:(i+1)*self.batch_size])
def __len__(self):
return len(self.dataset)//self.batch_size
然后重新定义读取数据的函数:
def load_data_fashion_mnist(batch_size, resize=None, root="~/.mxnet/datasets/fashion-mnist"):
mnist_train = gluon.data.vision.FashionMNIST(root=root, train=True, transform=None)
mnist_test = gluon.data.vision.FashionMNIST(root=root, train=False, transform=None)
train_data = DataLoader(mnist_train, batch_size, shuffle=True, resize=resize)
test_data = DataLoader(mnist_test, batch_size, shuffle=False, resize=resize)
return train_data, test_data
Intel MKL-DNN加速CPU计算
函数优化了之后,跑了挺久时间,感觉还是跑不出。无奈的我继续查资料,发现了MKL-DNN,Intel推出的深度神经网络的数学核心库。使用之后,在以前的简单卷积神经网络上测试了一下,发现速度快了一倍,对深度卷积神经网络上的加速作用应该会更大!
有两种方法安装MKL-DNN,一种是直接下载预先编译好的自带MKL-DNN的MXNet,另一种是自行对MXNet进行编译。
我采用的是简单便捷的第一种方法,用pip先卸载了原有的MXNet,然后安装预先编译过的MXNet。安装完后就可以直接用了。
pip uninstall mxnet
pip install --pre mxnet-mkl
RUN!
之前程序没跑出来,我一直担心是不是程序出现了bug,所以一心想让程序跑出结果来的我决定给CPU一个证明她自己的机会,狠心按下了RUN,任CPU放飞自我。
洗漱、洗衣、游走,时间悄然流逝,机器升温,风扇轰鸣,笔记本侧立散热,CPU 飞速 运转,真心为她捏了一把汗 。
历时23min,CPU利用率峰值一度达到100%,洗完衣服的我发现终端惊现一行输出:
epoch 1, loss 1.3076, train acc 0.511, test acc 0.737, time 1381.5 sec
我的CPU和我:😃
我又消磨了20分钟,机器跑完了第2个epoch,并且loss的下降和accuracy的升高表明,代码的确没有bug。限于时间问题和对CPU的关心,我决定不继续燃烧CPU了。
嗯,安心睡觉!