我想用简单的Python代码来分类手写数字(MNIST)。我的方法是使用一个简单的单层感知器,并且采用批处理方法进行训练。
我的问题是,例如,如果我先训练数字“1”,然后再训练其他数字,网络总是显示“1”的结果。实际上,训练只发生在第一个数字上。我不知道问题出在哪里。
我认为这可能与批处理训练有关,因为在一次训练后,第二个数字无法训练,因为网络已经收敛了。但我不知道如何解决这个问题。
我还测试了多层感知器,结果表现相同。
注意:每次我选择一个数字,加载大量该数字的样本并开始训练,对于其他数字,我会重新开始所有操作,除了权重矩阵(w0)之外。
这是我的代码:
1-导入库:
import os, structfrom array import array as pyarrayfrom numpy import append, array, int8, uint8, zerosimport numpy as npfrom IPython.display import Imageimport matplotlib.pyplot as pltfrom IPython import displayfrom scipy.special import expitfrom scipy.misc import imresizefrom IPython.core.page import pagefrom IPython.core.formatters import format_display_datanp.set_printoptions(threshold=np.nan)np.set_printoptions(suppress=True)
2- Sigmoid函数:
def sigmoid(x, deriv=False): if(deriv==True): return x*(1-x) return expit(x)
3- 初始化权重
np.random.seed(1)w0 = 2*np.random.random((784,10))-1
4- 读取MNIST数据集
dataset="training"path="."if dataset == "training": fname_img = os.path.join(path, 'train-images-idx3-ubyte') fname_lbl = os.path.join(path, 'train-labels-idx1-ubyte')elif dataset == "testing": fname_img = os.path.join(path, 't10k-images-idx3-ubyte') fname_lbl = os.path.join(path, 't10k-labels-idx1-ubyte')else: raise ValueError("dataset must be 'testing' or 'training'")flbl = open(fname_lbl, 'rb')magic_nr, size = struct.unpack(">II", flbl.read(8))lbl = pyarray("b", flbl.read())flbl.close()fimg = open(fname_img, 'rb')magic_nr, size, rows, cols = struct.unpack(">IIII", fimg.read(16))img = pyarray("B", fimg.read())fimg.close()
5- 选择一个数字
number = 4digits=[number]ind = [ k for k in range(size) if lbl[k] in digits ]N = len(ind)images = zeros((N, rows, cols), dtype=uint8)labels = zeros((N, 1), dtype=int8)for i in range(len(ind)): images[i] = array(img[ ind[i]*rows*cols : (ind[i]+1)*rows*cols ]).reshape((rows, cols)) labels[i] = lbl[ind[i]]
6- 将每个数字转换为向量,并将矩阵单元格转换为二进制:
p = np.reshape(images,(len(images),784))p[p > 0] = 1
7- 目标矩阵(每列对应一个数字)
t = np.zeros((len(images), 10),dtype=float)t[:,number] = 1
8- 训练(梯度下降)
for iter in xrange(600): predict = sigmoid(np.dot(p,w0)) e0 = predict - t delta0 = e0 * sigmoid(predict,True) w0 -= 0.01*np.dot(p.T,delta0)
9- 测试
test_predict = sigmoid(np.dot(p[102],w0))print test_predict
回答:
用单一类别(数字)的数据训练网络直到收敛,然后再添加另一个类别,这样做是没有意义的。
如果你只用一个类别进行训练,所需的输出总是相同的,网络可能会很快收敛。它可能会对所有类型的输入模式产生这种输出,而不仅仅是用于训练的那些模式。
你需要做的是在训练过程中呈现所有类别的输入,例如以随机顺序。这样,网络就能找到不同类别之间的边界。