@IVlad给了我非常有用的反馈后,我试着修改我的代码,修改后的部分看起来像:
syn0 = (2*np.random.random((784,len(train_sample))) - 1)/8
syn1 = (2*np.random.random((len(train_sample),10)) - 1)/8
for i in xrange(10000):
#forward propagation
l0=train_sample
l1=nonlin(np.dot(l0, syn0))
l2=nonlin(np.dot(l1, syn1))
#calculate error
l2_error=train_tag_bool-l2
if (i% 1000) == 0:
print "Error:" + str(np.mean(np.abs(l2_error)))
#apply sigmoid to the error
l2_delta = l2_error*nonlin(l2,deriv=True)
l1_error = l2_delta.dot(syn1.T)
l1_delta = l1_error * nonlin(l1,deriv=True)
#update weights
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次迭代之后,但我怀疑如果我运行1k或更多的循环,情况是否会有所不同。如果我返回每行的最大值,它总是第9个元素(代表数字'9'),这是完全错误的。
我又被这个问题难住了。溢出问题一直是我整个ML经验(当时是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,
原始代码:
import cPickle, gzip
import numpy as np
#from deeplearning.net
# Load the dataset
f = gzip.open('mnist.pkl.gz', 'rb')
train_set, valid_set, test_set = cPickle.load(f)
f.close()
#sigmoid function
def nonlin(x, deriv=False):
if (deriv ==True):
return x*(1-x)
return 1/(1+np.exp(-x))
#seed random numbers to make calculation
#deterministic (just a good practice)
np.random.seed(1)
#need to decrease the sample size or else computer dies
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's dimension for the pixels are 50000(samples) x 784 (28x28 for each sample)
#therefore the coefficients should be 784x50000 to make the hidden layer 50k x 50k
syn0 = 2*np.random.random((784,len(train_sample))) - 1
syn1 = 2*np.random.random((len(train_sample),1)) - 1
for i in xrange(10000):
#forward propagation
l0=train_sample
l1=nonlin(np.dot(l0, syn0))
l2=nonlin(np.dot(l1, syn1))
#calculate error
l2_error=train_tag-l2
if (i% 1000) == 0:
print "Error:" + str(np.mean(np.abs(l2_error)))
#apply sigmoid to the error
l2_delta = l2_error*nonlin(l2,deriv=True)
l1_error = l2_delta.dot(syn1.T)
l1_delta = l1_error * nonlin(l1,deriv=True)
#update weights
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) / 4
syn1 = (np.random.random((len(train_sample),1)) - 0.5) / 4
如果你感兴趣,你可以搜索更多的初始化方案,但我已经得到了不错的结果。
第四,正规化。最容易实现的可能是权重衰减。实现权重衰减lambda
可以这样做:
syn1 += alpha * l1.T.dot(l2_delta) - alpha * lambda * syn1
syn0 += 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, gzip
import numpy as np
#from deeplearning.net
# Load the dataset
f = gzip.open('mnist.pkl.gz', 'rb')
train_set, valid_set, test_set = pickle.load(f, encoding='latin1')
f.close()
#sigmoid function
def nonlin(x, deriv=False):
if (deriv ==True):
return 1-x*x
return np.tanh(x)
#seed random numbers to make calculation
#deterministic (just a good practice)
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_y
train_x, train_y = make_proper_pairs_from_set(train_set)
train_x = train_x
train_y = train_y
test_x, test_y = make_proper_pairs_from_set(test_set)
print(len(train_y))
#train_set's dimension for the pixels are 50000(samples) x 784 (28x28 for each sample)
#therefore the coefficients should be 784x50000 to make the hidden layer 50k x 50k
# changed to 200 hidden neurons, should be plenty
syn0 = (2*np.random.random((785,200)) - 1) / 10
syn1 = (2*np.random.random((201,10)) - 1) / 10
velocities0 = np.zeros(syn0.shape)
velocities1 = np.zeros(syn1.shape)
alpha = 0.01
beta = 0.0001
momentum = 0.99
m = len(train_x) # number of training samples
# moved the forward propagation to a function and added bias neurons
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 = 100
for i in range(num_epochs):
# forward propagation
l0, l1, l2, _ = forward_prop(train_x, m)
# calculate error
l2_error = l2 - train_y
print("Error " + str(i) + ": " + str(np.mean(np.abs(l2_error))))
# apply sigmoid to the error
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:]
# update weights
# divide gradients by the number of samples
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
# divide regularization by number of samples
# because L2 regularization reduces to this
syn1 += -v1 * momentum + (1 + momentum) * velocities1 - alpha * beta * syn1 / m
syn0 += -v0 * momentum + (1 + momentum) * velocities0 - alpha * beta * syn0 / m
# find accuracy on test set
predictions = []
corrects = []
for i in range(len(test_x)): # you can eliminate this loop too with a bit of work, but this part is very fast anyway
_, _, _, 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
,将epoch增加到1000
,则可以得到95.43%
的准确率。
用当前时间播种随机数生成器,添加更多隐藏神经元(或隐藏层)和更多参数调整可以使这个简单的模型达到大约98%
的精度AFAIK。问题是它训练起来太慢了。
而且,这种方法并不真正可靠。我优化了参数以提高测试集的准确性,所以我可能会过度拟合测试集。您应该使用交叉验证或验证集。
无论如何,正如您所看到的,没有溢出错误。如果您想更详细地讨论事情,请随时给我发电子邮件(地址在个人资料中)。