深度学习算法实践12---卷积神经网络(CNN)实现

在搞清楚卷积神经网络(CNN)的原理之后,在本篇博文中,我们将讨论基于Theano的算法实现技术。我们还将以MNIST手写数字识别为例,创建卷积神经网络(CNN),训练该网络,使识别误差达到1%以内。

我们首先需要读入MNIST手写数字识别的训练样本集,为此我们定义了一个工具类:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. __docformat__ = 'restructedtext en'  
  4.   
  5. import six.moves.cPickle as pickle  
  6. import gzip  
  7. import os  
  8. import sys  
  9. import timeit  
  10.   
  11. import numpy  
  12.   
  13. import theano  
  14. import theano.tensor as T  
  15.   
  16. class MnistLoader(object):  
  17.     def load_data(self, dataset):  
  18.         data_dir, data_file = os.path.split(dataset)  
  19.         if data_dir == "" and not os.path.isfile(dataset):  
  20.             new_path = os.path.join(  
  21.                 os.path.split(__file__)[0],  
  22.                 "..",  
  23.                 "data",  
  24.                 dataset  
  25.             )  
  26.             if os.path.isfile(new_path) or data_file == 'mnist.pkl.gz':  
  27.                 dataset = new_path  
  28.   
  29.         if (not os.path.isfile(dataset)) and data_file == 'mnist.pkl.gz':  
  30.             from six.moves import urllib  
  31.             origin = (  
  32.                 'http://www.iro.umontreal.ca/~lisa/deep/data/mnist/mnist.pkl.gz'  
  33.             )  
  34.             print('Downloading data from %s' % origin)  
  35.             urllib.request.urlretrieve(origin, dataset)  
  36.   
  37.         print('... loading data')  
  38.         # Load the dataset  
  39.         with gzip.open(dataset, 'rb') as f:  
  40.             try:  
  41.                 train_set, valid_set, test_set = pickle.load(f, encoding='latin1')  
  42.             except:  
  43.                 train_set, valid_set, test_set = pickle.load(f)  
  44.         def shared_dataset(data_xy, borrow=True):  
  45.             data_x, data_y = data_xy  
  46.             shared_x = theano.shared(numpy.asarray(data_x,  
  47.                                                dtype=theano.config.floatX),  
  48.                                  borrow=borrow)  
  49.             shared_y = theano.shared(numpy.asarray(data_y,  
  50.                                                dtype=theano.config.floatX),  
  51.                                  borrow=borrow)  
  52.             return shared_x, T.cast(shared_y, 'int32')  
  53.   
  54.         test_set_x, test_set_y = shared_dataset(test_set)  
  55.         valid_set_x, valid_set_y = shared_dataset(valid_set)  
  56.         train_set_x, train_set_y = shared_dataset(train_set)  
  57.   
  58.         rval = [(train_set_x, train_set_y), (valid_set_x, valid_set_y),  
  59.             (test_set_x, test_set_y)]  
  60.         return rval  
这个类在之前我们已经用过,在这里就不详细讲解了。之所以单独定义这个类,是因为如果我们将问题换为其他类型时,我们只需要修改这一个类,就可以实现训练数据的载入了,这样简化了程序修改工作量。

我们所采用的方法是将图像先接入卷积神经网络,之后再接入BP网络的隐藏层,然后再接入逻辑回归的输出层,因此我们需要先定义多层前向网络的隐藏层和逻辑回归输出层。隐藏层的定义如下所示:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. __docformat__ = 'restructedtext en'  
  4.   
  5.   
  6. import os  
  7. import sys  
  8. import timeit  
  9.   
  10. import numpy  
  11.   
  12. import theano  
  13. import theano.tensor as T  
  14.   
  15.   
  16. from logistic_regression import LogisticRegression  
  17.   
  18. # start-snippet-1  
  19. class HiddenLayer(object):  
  20.     def __init__(self, rng, input, n_in, n_out, W=None, b=None,  
  21.                  activation=T.tanh):  
  22.         self.input = input  
  23.         if W is None:  
  24.             W_values = numpy.asarray(  
  25.                 rng.uniform(  
  26.                     low=-numpy.sqrt(6. / (n_in + n_out)),  
  27.                     high=numpy.sqrt(6. / (n_in + n_out)),  
  28.                     size=(n_in, n_out)  
  29.                 ),  
  30.                 dtype=theano.config.floatX  
  31.             )  
  32.             if activation == theano.tensor.nnet.sigmoid:  
  33.                 W_values *= 4  
  34.   
  35.             W = theano.shared(value=W_values, name='W', borrow=True)  
  36.   
  37.         if b is None:  
  38.             b_values = numpy.zeros((n_out,), dtype=theano.config.floatX)  
  39.             b = theano.shared(value=b_values, name='b', borrow=True)  
  40.   
  41.         self.W = W  
  42.         self.b = b  
  43.   
  44.         lin_output = T.dot(input, self.W) + self.b  
  45.         self.output = (  
  46.             lin_output if activation is None  
  47.             else activation(lin_output)  
  48.         )  
  49.         # parameters of the model  
  50.         self.params = [self.W, self.b]  
接下来我们定义逻辑回归算法类:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. __docformat__ = 'restructedtext en'  
  4.   
  5. import six.moves.cPickle as pickle  
  6. import gzip  
  7. import os  
  8. import sys  
  9. import timeit  
  10.   
  11. import numpy  
  12.   
  13. import theano  
  14. import theano.tensor as T  
  15.   
  16. class LogisticRegression(object):    
  17.     def __init__(self, input, n_in, n_out):    
  18.         self.W = theano.shared(    
  19.             value=numpy.zeros(    
  20.                 (n_in, n_out),    
  21.                 dtype=theano.config.floatX    
  22.             ),    
  23.             name='W',    
  24.             borrow=True    
  25.         )    
  26.         self.b = theano.shared(    
  27.             value=numpy.zeros(    
  28.                 (n_out,),    
  29.                 dtype=theano.config.floatX    
  30.             ),    
  31.             name='b',    
  32.             borrow=True    
  33.         )    
  34.         self.p_y_given_x = T.nnet.softmax(T.dot(input, self.W) + self.b)    
  35.         self.y_pred = T.argmax(self.p_y_given_x, axis=1)    
  36.         self.params = [self.W, self.b]    
  37.         self.input = input    
  38.         print("Yantao: ***********************************")  
  39.     
  40.     def negative_log_likelihood(self, y):    
  41.         return -T.mean(T.log(self.p_y_given_x)[T.arange(y.shape[0]), y])    
  42.     
  43.     def errors(self, y):    
  44.         if y.ndim != self.y_pred.ndim:    
  45.             raise TypeError(    
  46.                 'y should have the same shape as self.y_pred',    
  47.                 ('y', y.type, 'y_pred'self.y_pred.type)    
  48.             )    
  49.         if y.dtype.startswith('int'):    
  50.             return T.mean(T.neq(self.y_pred, y))    
  51.         else:    
  52.             raise NotImplementedError()    

这段代码在逻辑回归博文中已经详细讨论过了,这里就不再重复了,有兴趣的读者可以查看这篇博文(逻辑回归算法实现)。

做完上述准备工作之后,我们就可以开始卷积神经网络(CNN)实现了。

我们先来定义基于简化版Lenet5的卷积神经网络(CNN)的定义,代码如下所示:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. import os  
  4. import sys  
  5. import timeit  
  6.   
  7. import numpy  
  8.   
  9. import theano  
  10. import theano.tensor as T  
  11. from theano.tensor.signal import pool  
  12. from theano.tensor.nnet import conv2d  
  13.   
  14.   
  15. class LeNetConvPoolLayer(object):  
  16.     def __init__(self, rng, input, filter_shape, image_shape, poolsize=(22)):  
  17.         assert image_shape[1] == filter_shape[1]  
  18.         self.input = input  
  19.         fan_in = numpy.prod(filter_shape[1:])  
  20.         fan_out = (filter_shape[0] * numpy.prod(filter_shape[2:]) //  
  21.                    numpy.prod(poolsize))  
  22.         W_bound = numpy.sqrt(6. / (fan_in + fan_out))  
  23.         self.W = theano.shared(  
  24.             numpy.asarray(  
  25.                 rng.uniform(low=-W_bound, high=W_bound, size=filter_shape),  
  26.                 dtype=theano.config.floatX  
  27.             ),  
  28.             borrow=True  
  29.         )  
  30.         b_values = numpy.zeros((filter_shape[0],), dtype=theano.config.floatX)  
  31.         self.b = theano.shared(value=b_values, borrow=True)  
  32.         conv_out = conv2d(  
  33.             input=input,  
  34.             filters=self.W,  
  35.             filter_shape=filter_shape,  
  36.             input_shape=image_shape  
  37.         )  
  38.         pooled_out = pool.pool_2d(  
  39.             input=conv_out,  
  40.             ds=poolsize,  
  41.             ignore_border=True  
  42.         )  
  43.         self.output = T.tanh(pooled_out + self.b.dimshuffle('x'0'x''x'))  
  44.         self.params = [self.W, self.b]  
  45.         self.input = input  
 上面代码实现了对输入信号的卷积操作,并对结果进行最大化池化。

下面我们来看怎样初始化Lenet层,怎样将Lenet层输出信号转为MLP网络隐藏层的输入信号,具体代码如下所示:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. layer0 = LeNetConvPoolLayer(  
  2.         rng,  
  3.         input=layer0_input,  
  4.         image_shape=(batch_size, 12828),  
  5.         filter_shape=(nkerns[0], 155),  
  6.         poolsize=(22)  
  7.     )  
如上所示,我们的输入信号是28*28的黑白图像,而且我们采用的批量学习,因此输入图像就定义为(batch_size, 1, 28, 28),我们对图像进行5*5卷积操作,根据卷积操作定义,最终得到的卷积输出层为(28-5+1,28-5+1)=(24,24)的“图像”,我们采用2*2的最大池化操作,即取2*2区域像素的最大值作为新的像素点的值,则最终输出层得到12*12的输出信号。

接下来,我们将输出信号继续输入一个Lenet卷积池化层,代码如下所示:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. layer1 = LeNetConvPoolLayer(  
  2.     rng,  
  3.     input=layer0.output,  
  4.     image_shape=(batch_size, nkerns[0], 1212),  
  5.     filter_shape=(nkerns[1], nkerns[0], 55),  
  6.     poolsize=(22)  
  7. )  
如上所示,这时输入信号变化为12*12的图像,我们还使用5*5的卷积核,可以得到(12-5+1, 12-5+1)=(8,8)的图像,采用2*2最大池化操作后,得到(4,4)图像。可以通过调用layer1.output.flatten(2)将其变为一维信号,从而输入MLP的隐藏层。

下面我们定义Lenet引擎来实现装入数据,定义网络模型,训练网络工作,代码如下所示:

[python] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. from __future__ import print_function  
  2.   
  3. import os  
  4. import sys  
  5. import timeit  
  6.   
  7. import numpy  
  8.   
  9. import theano  
  10. import theano.tensor as T  
  11. from theano.tensor.signal import pool  
  12. from theano.tensor.nnet import conv2d  
  13.   
  14. from mnist_loader import MnistLoader  
  15. from logistic_regression import LogisticRegression  
  16. from hidden_layer import HiddenLayer  
  17. from lenet_conv_pool_layer import LeNetConvPoolLayer  
  18.   
  19. class LenetMnistEngine(object):  
  20.     def __init__(self):  
  21.         print("create LenetMnistEngine")  
  22.   
  23.     def train_model(self):  
  24.         learning_rate = 0.1  
  25.         n_epochs = 200  
  26.         dataset = 'mnist.pkl.gz'  
  27.         nkerns = [2050]  
  28.         batch_size = 500  
  29.         (n_train_batches, n_test_batches, n_valid_batches, \  
  30.                     train_model, test_model, validate_model) = \  
  31.                     self.build_model(learning_rate, n_epochs, \  
  32.                         dataset, nkerns, batch_size)  
  33.         self.train(n_epochs, n_train_batches, n_test_batches, \  
  34.                     n_valid_batches, train_model, test_model, \  
  35.                     validate_model)  
  36.   
  37.     def run(self):  
  38.         print("run the model")  
  39.         classifier = pickle.load(open('best_model.pkl''rb'))  
  40.         predict_model = theano.function(  
  41.             inputs=[classifier.input],  
  42.             outputs=classifier.logRegressionLayer.y_pred  
  43.         )  
  44.         dataset='mnist.pkl.gz'  
  45.         loader = MnistLoader()  
  46.         datasets = loader.load_data(dataset)  
  47.         test_set_x, test_set_y = datasets[2]  
  48.         test_set_x = test_set_x.get_value()  
  49.         predicted_values = predict_model(test_set_x[:10])  
  50.         print("Predicted values for the first 10 examples in test set:")  
  51.         print(predicted_values)  
  52.   
  53.     def build_model(self, learning_rate=0.1, n_epochs=200,  
  54.                         dataset='mnist.pkl.gz',  
  55.                         nkerns=[2050], batch_size=500):  
  56.         rng = numpy.random.RandomState(23455)  
  57.         loader = MnistLoader()  
  58.         datasets = loader.load_data(dataset)  
  59.         train_set_x, train_set_y = datasets[0]  
  60.         valid_set_x, valid_set_y = datasets[1]  
  61.         test_set_x, test_set_y = datasets[2]  
  62.         n_train_batches = train_set_x.get_value(borrow=True).shape[0]  
  63.         n_valid_batches = valid_set_x.get_value(borrow=True).shape[0]  
  64.         n_test_batches = test_set_x.get_value(borrow=True).shape[0]  
  65.         n_train_batches //= batch_size  
  66.         n_valid_batches //= batch_size  
  67.         n_test_batches //= batch_size  
  68.         index = T.lscalar()   
  69.         x = T.matrix('x')     
  70.         y = T.ivector('y')   
  71.         print('... building the model')  
  72.         layer0_input = x.reshape((batch_size, 12828))  
  73.         layer0 = LeNetConvPoolLayer(  
  74.             rng,  
  75.             input=layer0_input,  
  76.             image_shape=(batch_size, 12828),  
  77.             filter_shape=(nkerns[0], 155),  
  78.             poolsize=(22)  
  79.         )  
  80.         layer1 = LeNetConvPoolLayer(  
  81.             rng,  
  82.             input=layer0.output,  
  83.             image_shape=(batch_size, nkerns[0], 1212),  
  84.             filter_shape=(nkerns[1], nkerns[0], 55),  
  85.             poolsize=(22)  
  86.         )  
  87.         layer2_input = layer1.output.flatten(2)  
  88.         layer2 = HiddenLayer(  
  89.             rng,  
  90.             input=layer2_input,  
  91.             n_in=nkerns[1] * 4 * 4,  
  92.             n_out=500,  
  93.             activation=T.tanh  
  94.         )  
  95.         layer3 = LogisticRegression(input=layer2.output, n_in=500, n_out=10)  
  96.         cost = layer3.negative_log_likelihood(y)  
  97.         test_model = theano.function(  
  98.             [index],  
  99.             layer3.errors(y),  
  100.             givens={  
  101.                 x: test_set_x[index * batch_size: (index + 1) * batch_size],  
  102.                 y: test_set_y[index * batch_size: (index + 1) * batch_size]  
  103.             }  
  104.         )  
  105.         validate_model = theano.function(  
  106.             [index],  
  107.             layer3.errors(y),  
  108.             givens={  
  109.                 x: valid_set_x[index * batch_size: (index + 1) * batch_size],  
  110.                 y: valid_set_y[index * batch_size: (index + 1) * batch_size]  
  111.             }  
  112.         )  
  113.         params = layer3.params + layer2.params + layer1.params + layer0.params  
  114.         grads = T.grad(cost, params)  
  115.         updates = [  
  116.             (param_i, param_i - learning_rate * grad_i)  
  117.             for param_i, grad_i in zip(params, grads)  
  118.         ]  
  119.         train_model = theano.function(  
  120.             [index],  
  121.             cost,  
  122.             updates=updates,  
  123.             givens={  
  124.                 x: train_set_x[index * batch_size: (index + 1) * batch_size],  
  125.                 y: train_set_y[index * batch_size: (index + 1) * batch_size]  
  126.             }  
  127.         )  
  128.         return (n_train_batches, n_test_batches, n_valid_batches, \  
  129.                     train_model, test_model, validate_model)  
  130.   
  131.     def train(self, n_epochs, n_train_batches, n_test_batches, n_valid_batches,   
  132.                 train_model, test_model, validate_model):  
  133.         print('... training')  
  134.         patience = 10000  
  135.         patience_increase = 2  
  136.         improvement_threshold = 0.995  
  137.         validation_frequency = min(n_train_batches, patience // 2)  
  138.         best_validation_loss = numpy.inf  
  139.         best_iter = 0  
  140.         test_score = 0.  
  141.         start_time = timeit.default_timer()  
  142.         epoch = 0  
  143.         done_looping = False  
  144.         while (epoch < n_epochs) and (not done_looping):  
  145.             epoch = epoch + 1  
  146.             for minibatch_index in range(n_train_batches):  
  147.                 iter = (epoch - 1) * n_train_batches + minibatch_index  
  148.                 if iter % 100 == 0:  
  149.                     print('training @ iter = ', iter)  
  150.                 cost_ij = train_model(minibatch_index)  
  151.                 if (iter + 1) % validation_frequency == 0:  
  152.                     validation_losses = [validate_model(i) for i  
  153.                                          in range(n_valid_batches)]  
  154.                     this_validation_loss = numpy.mean(validation_losses)  
  155.                     print('epoch %i, minibatch %i/%i, validation error %f %%' %  
  156.                           (epoch, minibatch_index + 1, n_train_batches,  
  157.                            this_validation_loss * 100.))  
  158.                     if this_validation_loss < best_validation_loss:  
  159.                         if this_validation_loss < best_validation_loss *  \  
  160.                            improvement_threshold:  
  161.                             patience = max(patience, iter * patience_increase)  
  162.                         best_validation_loss = this_validation_loss  
  163.                         best_iter = iter  
  164.                         test_losses = [  
  165.                             test_model(i)  
  166.                             for i in range(n_test_batches)  
  167.                         ]  
  168.                         test_score = numpy.mean(test_losses)  
  169.                         with open('best_model.pkl''wb') as f:  
  170.                             pickle.dump(classifier, f)  
  171.                         print(('     epoch %i, minibatch %i/%i, test error of '  
  172.                                'best model %f %%') %  
  173.                               (epoch, minibatch_index + 1, n_train_batches,  
  174.                                test_score * 100.))  
  175.                 if patience <= iter:  
  176.                     done_looping = True  
  177.                     break  
  178.         end_time = timeit.default_timer()  
  179.         print('Optimization complete.')  
  180.         print('Best validation score of %f %% obtained at iteration %i, '  
  181.               'with test performance %f %%' %  
  182.               (best_validation_loss * 100., best_iter + 1, test_score * 100.))  
  183.         print(('The code for file ' +  
  184.                os.path.split(__file__)[1] +  
  185.                ' ran for %.2fm' % ((end_time - start_time) / 60.)), file=sys.stderr)  
上述代码与之前的MLP的训练代码类似,这里就不再讨论了。在我的Mac笔记本上,运行大约6个小时,会得到错误率小于1%的结果。
阅读更多
个人分类: 深度学习
上一篇深度学习算法实践11---卷积神经网络(CNN)之卷积操作
下一篇深度学习算法实践15---堆叠去噪自动编码机(SdA)原理及实现
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭