因为left_eye_center_x要和right_eye_center_x换位置,我们记录(0,2),同样left_eye_center_y要和right_eye_center_y换位置,我们记录元组(1,3),以此类推。最后,我们获得元组集合如下:
flip_indices = [
(0, 2), (1, 3),
(4, 8), (5, 9), (6, 10), (7, 11),
(12, 16), (13, 17), (14, 18), (15, 19),
(22, 24), (23, 25),
]
# Let's see if we got it right:
df = read_csv(os.path.expanduser(FTRAIN))
for i, j in flip_indices:
print("# {} -> {}".format(df.columns[i], df.columns[j]))
# this prints out:
# left_eye_center_x -> right_eye_center_x
# left_eye_center_y -> right_eye_center_y
# left_eye_inner_corner_x -> right_eye_inner_corner_x
# left_eye_inner_corner_y -> right_eye_inner_corner_y
# left_eye_outer_corner_x -> right_eye_outer_corner_x
# left_eye_outer_corner_y -> right_eye_outer_corner_y
# left_eyebrow_inner_end_x -> right_eyebrow_inner_end_x
# left_eyebrow_inner_end_y -> right_eyebrow_inner_end_y
# left_eyebrow_outer_end_x -> right_eyebrow_outer_end_x
# left_eyebrow_outer_end_y -> right_eyebrow_outer_end_y
# mouth_left_corner_x -> mouth_right_corner_x
# mouth_left_corner_y -> mouth_right_corner_y
我们的批处理迭代器的实现将会从BachIterator类派生,重载transform()方法。把这些东西组合到一起,看看完整的代码:
from nolearn.lasagne import BatchIterator
class FlipBatchIterator(BatchIterator):
flip_indices = [
(0, 2), (1, 3),
(4, 8), (5, 9), (6, 10), (7, 11),
(12, 16), (13, 17), (14, 18), (15, 19),
(22, 24), (23, 25),
]
def transform(self, Xb, yb):
Xb, yb = super(FlipBatchIterator, self).transform(Xb, yb)
# Flip half of the images in this batch at random:
bs = Xb.shape[0]
indices = np.random.choice(bs, bs / 2, replace=False)
Xb[indices] = Xb[indices, :, :, ::-1]
if yb is not None:
# Horizontal flip of all x coordinates:
yb[indices, ::2] = yb[indices, ::2] * -1
# Swap places, e.g. left_eye_center_x -> right_eye_center_x
for a, b in self.flip_indices:
yb[indices, a], yb[indices, b] = (
yb[indices, b], yb[indices, a])
return Xb, yb
使用上述批处理迭代器进行训练,需要把它作为batch_iterator_train参数传递给NeuralNet。让我们来定义net3,一个和net2非常相似的网络。仅仅是在网络的最后添加了这些行:
net3 = NeuralNet(
# ...
regression=True,
batch_iterator_train=FlipBatchIterator(batch_size=128),
max_epochs=3000,
verbose=1,
)
现在我们已经采用了最新的翻转技巧,但同时我们将迭代次数增长了三倍。因为我们并没有真正改变训练集合的总数,所以每个epoch仍然使用和刚刚一样多的样本个数。事实证明,采用了新技巧后,每个训练epoch还是比刚刚多用了一些时间。这次我们的网络学到的东西更具一般性,理论上来讲学习更一般性的规律比学出过拟合总是要更难一些。
这次网络将花费一小时的训练时间,我们要确保在训练之后,把得到的模型保存起来。然后就可以去喝杯茶或者做做家务活,洗衣服也是不错的选择。
net3.fit(X, y)
import cPickle as pickle
with open('net3.pickle', 'wb') as f:
pickle.dump(net3, f, -1)
1
2
3
4
5
1
2
3
4
5
$ python kfkd.py
...
Epoch | Train loss | Valid loss | Train / Val
--------|--------------|--------------|----------------
...
500 | 0.002238 | 0.002303 | 0.971519
...
1000 | 0.001365 | 0.001623 | 0.841110
1500 | 0.001067 | 0.001457 | 0.732018
2000 | 0.000895 | 0.001369 | 0.653721
2500 | 0.000761 | 0.001320 | 0.576831
3000 | 0.000678 | 0.001288 | 0.526410
让我们画出学习曲线和net2对比。应该看到3000次迭代后的效果,net3比net2的验证损失要小了5%。我们看到在2000次迭代之后,net2已经停止学习了,并且曲线变得不光滑;但是net3的效果却在一直改进,尽管进展缓慢。
评论