神经网络在MNIST上的应用–结果未达预期

在@IVlad 给了我非常有用的反馈后,我尝试修改了我的代码,修改后的部分看起来像这样:

syn0 = (2*np.random.random((784,len(train_sample))) - 1)/8syn1 = (2*np.random.random((len(train_sample),10)) - 1)/8for i in xrange(10000):    #前向传播    l0=train_sample    l1=nonlin(np.dot(l0, syn0))    l2=nonlin(np.dot(l1, syn1))    #计算误差    l2_error=train_tag_bool-l2    if (i% 1000) == 0:        print "Error:" + str(np.mean(np.abs(l2_error)))    #对误差应用sigmoid函数     l2_delta = l2_error*nonlin(l2,deriv=True)    l1_error = l2_delta.dot(syn1.T)    l1_delta = l1_error * nonlin(l1,deriv=True)    #更新权重    syn1 += alpha* (l1.T.dot(l2_delta) - beta*syn1)    syn0 += alpha* (l0.T.dot(l1_delta) - beta*syn0)

请注意,现在的标签(真实标签)是一个<3000 x 10>的矩阵,每一行为一个样本,十列描述每个样本代表哪个数字。(train_tag_bool,现在回想起来,它并不是布尔格式,所以命名不太好,但为了讨论方便,我暂时保持这种方式。)

在这个项目中,我只在输入层和输出层之间使用了一个隐藏层,希望这足以完成任务。我应用了学习率和权重衰减,并且使初始权重稍微小一些。

在计算误差率时,我使用了网站上的代码,即

np.mean(np.abs(l2_error))

结果是0.1。我不确定这意味着什么。

此外,我进入了l2层(应该是给出预测的输出层),所有的值都非常小(每个样本的最大值小于10^-9,最小值可以达到10^-85)。这只是在5次迭代之后,但我怀疑即使我运行1000次循环或更多,情况也不会有任何不同。如果我返回每一行的最大值,它总是第9个元素(代表数字‘9’),这完全是错误的。

我再次在这个问题上卡住了。溢出问题一直是我整个机器学习经历中最大的挑战(当时是MATLAB,不是Numpy),我还没有找到解决它的方法…..


train_tag_bool代码:

train_tag_bool=np.array([[0]*10]*len(train_tag)).astype('float64')for i in range(len(train_tag)):    if train_tag[i]==0:        train_tag_bool[i][0]=1    elif train_tag[i]==1:        train_tag_bool[i][1]=1    elif train_tag[i]==2:        train_tag_bool[i][2]=1    elif train_tag[i]==3:        train_tag_bool[i][3]=1    elif train_tag[i]==4:        train_tag_bool[i][4]=1    elif train_tag[i]==5:        train_tag_bool[i][5]=1    elif train_tag[i]==6:        train_tag_bool[i][6]=1    elif train_tag[i]==7:        train_tag_bool[i][7]=1    elif train_tag[i]==8:        train_tag_bool[i][8]=1    elif train_tag[i]==9:        train_tag_bool[i][9]=1

我知道这是蛮力方法,但这目前不是我最关心的问题。结果是一个3000 x 10的矩阵,其中1对应于每个样本的数字。第一个元素代表数字0,最后一个代表9

例如,[0 0 0 0 0 0 1 0 0 0]代表6,[1 0 0 0 0 0 0 0 0 0]代表0。

原始代码:

import cPickle, gzipimport numpy as np#from deeplearning.net# Load the datasetf = gzip.open('mnist.pkl.gz', 'rb')train_set, valid_set, test_set = cPickle.load(f)f.close()#sigmoid函数def nonlin(x, deriv=False):    if (deriv ==True):        return x*(1-x)    return 1/(1+np.exp(-x))#种子随机数以使计算#确定性(这是一个好习惯)np.random.seed(1)#需要减少样本大小,否则计算机会死机train_sample=train_set[0][0:3000]train_tag=train_set[1][0:3000]train_tag=train_tag.reshape(len(train_tag), 1)#train_set的像素维度为50000(样本)x 784(每个样本28x28)#因此系数应该是784x50000,使隐藏层为50k x 50ksyn0 = 2*np.random.random((784,len(train_sample))) - 1syn1 = 2*np.random.random((len(train_sample),1)) - 1for i in xrange(10000):    #前向传播    l0=train_sample    l1=nonlin(np.dot(l0, syn0))    l2=nonlin(np.dot(l1, syn1))    #计算误差    l2_error=train_tag-l2    if (i% 1000) == 0:        print "Error:" + str(np.mean(np.abs(l2_error)))    #对误差应用sigmoid函数     l2_delta = l2_error*nonlin(l2,deriv=True)    l1_error = l2_delta.dot(syn1.T)    l1_delta = l1_error * nonlin(l1,deriv=True)    #更新权重    syn1 += l1.T.dot(l2_delta)    syn0 += l0.T.dot(l1_delta)

参考资料:

http://iamtrask.github.io/2015/07/12/basic-python-network/

http://yann.lecun.com/exdb/mnist/


回答:

我目前无法运行代码,但有几点引人注目。我很惊讶它即使在博客中使用的玩具问题上也能很好地工作。

在我们开始之前,你需要更多的输出神经元:确切地说是10个。

syn1 = 2*np.random.random((len(train_sample), 10)) - 1

你的标签(y)最好是一个长度为10的数组,在正确数字的位置有一个1,其他位置为0

首先,我默认尝试的一件事是尽可能使用float64…这几乎从不改变任何事情,所以我不确定你是否应该养成这个习惯。可能不应该。

其次,该代码没有你可以设置的学习率。这意味着学习率隐式为1,对于你的问题来说,这太大了,人们使用0.01甚至更小。要添加学习率alpha,请执行以下操作:

syn1 += alpha * l1.T.dot(l2_delta)syn0 += alpha * l0.T.dot(l1_delta)

并将其设置为最多0.01。你需要调整它以获得最佳结果。

第三,通常最好用小权重初始化网络。[0, 1)可能太大了。尝试:

syn0 = (np.random.random((784,len(train_sample))) - 0.5) / 4syn1 = (np.random.random((len(train_sample),1)) - 0.5) / 4

如果你有兴趣,可以搜索更多复杂的初始化方案,但我已经用上面的方法得到了不错的结果。

第四,正则化。最容易实现的可能是权重衰减。实现权重衰减lambda可以这样做:

syn1 += alpha * l1.T.dot(l2_delta) - alpha * lambda * syn1syn0 += alpha * l0.T.dot(l1_delta) - alpha * lambda * syn0

常用值也是< 0.1甚至< 0.01

Dropout也可能有帮助,但在我看来,如果你刚开始,这有点难实现和理解。对于更深的网络来说,它也更有用。所以也许把它留到最后。

第五,可能还可以使用动量(在权重衰减链接中解释),这应该能减少你的网络的学习时间。还要调整迭代次数:你不希望太多,但也不要太少。

第六,查看softmax作为输出层。

第七,查看tanh而不是你当前的nonlin sigmoid函数。

如果你逐步应用这些,你应该会开始得到一些有意义的结果。我认为正则化和较小的初始权重应该有助于解决溢出错误。

更新:

我已经这样更改了代码。经过仅100次训练轮次,准确率为84.79%。几乎没有调整任何东西,结果还不错。

我添加了偏置神经元、动量、权重衰减,使用了更少的隐藏单元(你之前的速度太慢了),改成了tanh函数和其他一些改动。

你应该可以从这里进一步调整它。我使用的是Python 3.4,所以我不得不做一些小的改动才能运行它,但这没什么大不了的。

import pickle, gzipimport numpy as np#from deeplearning.net# Load the datasetf = gzip.open('mnist.pkl.gz', 'rb')train_set, valid_set, test_set = pickle.load(f, encoding='latin1')f.close()#sigmoid函数def nonlin(x, deriv=False):    if (deriv ==True):        return 1-x*x    return np.tanh(x)#种子随机数以使计算#确定性(这是一个好习惯)np.random.seed(1)def make_proper_pairs_from_set(data_set):    data_set_x, data_set_y = data_set    data_set_y = np.eye(10)[:, data_set_y].T    return data_set_x, data_set_ytrain_x, train_y = make_proper_pairs_from_set(train_set)train_x = train_xtrain_y = train_ytest_x, test_y = make_proper_pairs_from_set(test_set)print(len(train_y))#train_set的像素维度为50000(样本)x 784(每个样本28x28)#因此系数应该是784x50000,使隐藏层为50k x 50k#改为200个隐藏神经元,应该足够syn0 = (2*np.random.random((785,200)) - 1) / 10syn1 = (2*np.random.random((201,10)) - 1) / 10velocities0 = np.zeros(syn0.shape)velocities1 = np.zeros(syn1.shape)alpha = 0.01beta = 0.0001momentum = 0.99m = len(train_x) # 训练样本数量#将前向传播移到一个函数中并添加偏置神经元def forward_prop(set_x, m):    l0 = np.c_[np.ones((m, 1)), set_x]    l1 = nonlin(np.dot(l0, syn0))    l1 = np.c_[np.ones((m, 1)), l1]    l2 = nonlin(np.dot(l1, syn1))    return l0, l1, l2, l2.argmax(axis=1)num_epochs = 100for i in range(num_epochs):    # 前向传播    l0, l1, l2, _ = forward_prop(train_x, m)    # 计算误差    l2_error = l2 - train_y    print("Error " + str(i) + ": " + str(np.mean(np.abs(l2_error))))    # 对误差应用sigmoid函数     l2_delta = l2_error * nonlin(l2,deriv=True)    l1_error = l2_delta.dot(syn1.T)    l1_delta = l1_error * nonlin(l1,deriv=True)    l1_delta = l1_delta[:, 1:]    # 更新权重    # 将梯度除以样本数量    grad0 = l0.T.dot(l1_delta) / m    grad1 = l1.T.dot(l2_delta) / m    v0 = velocities0    v1 = velocities1    velocities0 = velocities0 * momentum - alpha * grad0    velocities1 = velocities1 * momentum - alpha * grad1    # 将正则化除以样本数量    # 因为L2正则化简化为此    syn1 += -v1 * momentum + (1 + momentum) * velocities1 - alpha * beta * syn1 / m    syn0 += -v0 * momentum + (1 + momentum) * velocities0 - alpha * beta * syn0 / m# 在测试集上查找准确率predictions = []corrects = []for i in range(len(test_x)): # 你也可以消除这个循环,但这部分非常快    _, _, _, rez = forward_prop([test_x[i, :]], 1)    predictions.append(rez[0])    corrects.append(test_y[i].argmax())predictions = np.array(predictions)corrects = np.array(corrects)print(np.sum(predictions == corrects) / len(test_x))

更新2:

如果你将学习率增加到0.05,并将轮次增加到1000,你会得到95.43%的准确率。

用当前时间种子随机数生成器,增加更多的隐藏神经元(或隐藏层)和更多的参数调整,可以使这个简单的模型达到大约98%的准确率,据我所知。问题是训练速度慢。

此外,这种方法论并不是很合理。我优化了参数以提高测试集上的准确率,所以我可能在过拟合测试集。你应该使用交叉验证或验证集。

无论如何,正如你所看到的,没有溢出错误。如果你想更详细地讨论这些问题,请随时通过电子邮件与我联系(地址在个人资料中)。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注