首页 > 技术文章 > 改进神经网络的学习方法

qw12 2016-11-28 20:21 原文

 

防止输出层梯度饱和

1.sigmoid+交叉熵 

均方误差做代价函数时,输出层反向传播的误差$$\delta ^{L} = (a-y)*{\sigma}'$$

当sigmoid函数饱和时,即使a与y相差很大,传播误差也很小,造成前期训练速度很慢。

因此上一篇随笔中采用了交叉熵作为输出层代价函数。

2.softmax+对数似然 

使用softmax代替sigmoid作为输出层,激活值为

输入样本的 log-likelihood 代价函数就是

反向传播误差与sigmoid+交叉熵形式相同,但aj取值不同。

下面的代码中添加了softmax,在小训练集上效果与方法1接近。

防止过度拟合

1.增加样本

随着训练集样本从1000到5000到50000,测试数据的准确率会越来越高。

当样本数无法增加时,可以采用图像平移、旋转来扩大样本数据。

2.正则化

常用L2 规范化,想法是增加一个额外的项到代价函数上,这个项叫做规范化项。

下面是规范化交叉熵

求偏导得到下面结果,权重更新方式不变。

下面的代码中添加了L2 正则化,并修改了cost计算。

权重初始化

之前的初始化方式(np.random.randn(y, x))就是根据独立的均值为 0,标准差为 1 的高斯随机变量随机采样作为权重和偏差的初始值。

单元输入加权和z有一个非常宽的高斯分布,输出σ(z)就会接近 1或者 0,表示我们的隐藏元会饱和。

使用均值为0标准差为 1/√n的高斯分布(np.random.randn(y, x)/np.sqrt(x))初始化这些权重,能让我们的神经元更不可能饱和。

下面的代码中添加了改进的权重初始化。

 

  1 # coding:utf8
  2 import cPickle
  3 import numpy as np
  4 import matplotlib.pyplot as plt
  5 
  6 class Network(object):
  7     def __init__(self, sizes):
  8         self.num_layers = len(sizes)
  9         self.sizes = sizes
 10         self.biases = [np.random.randn(y, 1) for y in sizes[1:]]  # L(n-1)->L(n)
 11         # self.weights = [np.random.randn(y, x)
 12         self.weights = [np.random.randn(y, x)/np.sqrt(x)  # improved weight initializer
 13                         for x, y in zip(sizes[:-1], sizes[1:])]
 14 
 15     def feedforward(self, a):
 16         for b_, w_ in zip(self.biases[:-1], self.weights[:-1]):
 17             a = self.sigmoid(np.dot(w_, a)+b_)
 18         a=self.sigmoid(np.dot(self.weights[-1], a)+self.biases[-1])
 19         # a=self.softmax(np.dot(self.weights[-1], a)+self.biases[-1])  # add for softmax
 20         return a
 21 
 22     def SGD(self, training_data, test_data,epochs, mini_batch_size, eta=1.0, lambda_=0.1):
 23         n_test = len(test_data)
 24         n = len(training_data)
 25         plt.xlabel('epoch')
 26         plt.ylabel('value')
 27         plt.title('cost')
 28         cy=[]
 29         cx=range(epochs)
 30         for j in cx:
 31             self.cost = 0.0
 32             np.random.shuffle(training_data)  # shuffle
 33             for k in xrange(0, n, mini_batch_size):
 34                 mini_batch = training_data[k:k+mini_batch_size]
 35                 self.update_mini_batch(mini_batch, eta,n)
 36             self.cost+=0.5*lambda_*sum(np.linalg.norm(w_)**2 for w_ in self.weights)
 37             cy.append(self.cost/n)
 38             print "Epoch {0}: {1} / {2}, {3} / {4}".format(
 39                     j, self.evaluate(training_data,1), n,self.evaluate(test_data), n_test)
 40         plt.plot(cx,cy)
 41         plt.scatter(cx,cy)
 42         plt.show()
 43 
 44     def update_mini_batch(self, mini_batch, eta, n, lambda_=0.1):
 45         for x, y in mini_batch:
 46             delta_b, delta_w = self.backprop(x, y)
 47             for i in range(len(self.weights)):  # L2 regularization
 48                 self.weights[i]-=eta/len(mini_batch)*(delta_w[i] +lambda_/n*self.weights[i])
 49             self.biases -= eta/len(mini_batch)*delta_b
 50             a=self.feedforward(x)
 51             cost=np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))
 52             self.cost += cost
 53 
 54     def backprop(self, x, y):
 55         b=np.zeros_like(self.biases)
 56         w=np.zeros_like(self.weights)
 57         a_ = x
 58         a = [x]
 59         for b_, w_ in zip(self.biases, self.weights):
 60             a_ = self.sigmoid(np.dot(w_, a_)+b_)
 61             a.append(a_)
 62         for l in xrange(1, self.num_layers):
 63             if l==1:
 64                 # delta= self.sigmoid_prime(a[-1])*(a[-1]-y)  # O(k)=a[-1], t(k)=y
 65                 delta= a[-1]-y  # cross-entropy
 66                 # delta=self.softmax(np.dot(w_, a[-2])+b_)  -y  # add for softmax
 67             else:
 68                 sp = self.sigmoid_prime(a[-l])   # O(j)=a[-l]
 69                 delta = np.dot(self.weights[-l+1].T, delta) * sp
 70             b[-l] = delta
 71             w[-l] = np.dot(delta, a[-l-1].T)
 72         return (b, w)
 73 
 74     def evaluate(self, test_data, train=0):
 75         test_results = [(np.argmax(self.feedforward(x)), y)
 76                         for (x, y) in test_data]
 77         if train:
 78             return sum(int(x == np.argmax(y)) for (x, y) in test_results)
 79         else:
 80             return sum(int(x == y) for (x, y) in test_results)
 81 
 82     def sigmoid(self,z):
 83         return 1.0/(1.0+np.exp(-z))
 84 
 85     def sigmoid_prime(self,z):
 86         return z*(1-z)
 87 
 88     def softmax(self,a):
 89         m = np.exp(a)
 90         return m / np.sum(m)
 91 
 92 def get_label(i):
 93     c=np.zeros((10,1))
 94     c[i]=1
 95     return c
 96 
 97 if __name__ == '__main__':
 98         def get_data(data):
 99             return [np.reshape(x, (784,1)) for x in data[0]]
100 
101         f = open('mnist.pkl', 'rb')
102         training_data, validation_data, test_data = cPickle.load(f)
103         training_inputs = get_data(training_data)
104         training_label=[get_label(y_) for y_ in training_data[1]]
105         data = zip(training_inputs,training_label)
106         test_inputs = training_inputs = get_data(test_data)
107         test = zip(test_inputs,test_data[1])
108         net = Network([784, 30, 10])
109         net.SGD(data[:5000],test[:5000],epochs=20,mini_batch_size=10, eta=0.5, lambda_=0.1)
110         # Epoch 19: 4992 / 5000 (train data), 4507 / 5000 (test data)

 

 

推荐阅读