改进神经网络的学习方法
防止输出层梯度饱和
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)