这是一个手把手教你学习深度校园的教程。一步一步,咱们即将测验去处理Kaggle challenge中的脸部要害点的检测问题。
这份教程介绍了Lasagne,一个比较新的依据Python和Theano的神经网络库。咱们将用Lasagne去模仿一系列的神经网络结构,评论一下数据增强(data augmentaTIon)、丢失(dropout)、结合动量(momentum)和预先练习(pre-training)。这儿有许多办法能够将咱们的成果改善不少。
我假定诸位现已知道了一些关于神经网络的仅仅。所以咱们就不介绍神经网络的布景常识了。这儿也供给一些好的介绍神经网络的书本和视频,如Neural Networks and Deep Learning online book。Alec Radford的讲演Deep Learning with Python’s Theano library也是一个快速介绍的好比方。以及ConvNetJS Browser Demos
预先预备
假如你只需求看懂的话,则不需求自己写一个代码然后去履行。这儿供给一些装置的教程给那些装备好CUDA的GPU而且想要运转实验的那些人。
我假定你们现已装置了CUDA toolkit, Python 2.7.x, numpy, pandas, matplotlib, 和scikit-learn。装置剩余的依靠包,比方Lasagne和Theano都能够运转下面的指令
pip install -r https://raw.githubusercontent.com/dnouri/kfkd-tutorial/master/requiremen…
留意,为了简练起见,我没有在指令中创立虚拟环境,可是你需求的。
译者:我是在windows10上面装备这个环境的,装置anaconda(再用此环境装置依靠包)、VS2013(不引荐2015)、CUDA东西即可。
假如一切都顺畅的话,你将会在你的虚拟环境下的src/lasagne/examples/目录中找到mnist.py并运转MNIST比方。这是一个关于神经网络的“Hello world”程序。数据中有十个分类,分别是0~9的数字,输入时28&TImes;28的手写数字图片。
cd src/lasagne/examples/
python mnist.py
此指令将在三十秒左右后开端打印输出。 这需求一段时刻的原因是,Lasagne运用Theano做重型起重; Theano反过来是一个“优化GPU元编程代码生成面向数组的优化Python数学编译器”,它将生成需求在练习发生前编译的C代码。 走运的是,咱们组需求在第一次运转时付出这个开支的价格。
译者:假如没有装备GPU,用的是CPU的话,应该是不必这么久的编译时刻,可是履行时刻有一些长。假如用GPU,在第一次跑一些程序的时分,会有提示正在编译的内容。
当练习开端的时分,你会看到
Epoch 1 of 500
training loss: 1.352731
validaTIon loss: 0.466565
validaTIon accuracy: 87.70 %
Epoch 2 of 500
training loss: 0.591704
validation loss: 0.326680
validation accuracy: 90.64 %
Epoch 3 of 500
training loss: 0.464022
validation loss: 0.275699
validation accuracy: 91.98 %
…
假如你让练习运转满足长,你会留意到,在大约75代之后,它将到达大约98%的测验精度。
假如你用的是GPU,你想要让Theano去运用它,你要在用户的主文件夹下面创立一个.theanorc文件。你需求依据自己装置环境以及自己操作系统的装备运用不同的装备信息:
[global]
floatX = float32
device = gpu0
[lib]
cnmem = 1
译者:这是我的装备文件。
[cuba]
root = C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0
[global]
openmp = False
device = gpu
floatX = float32
allow_input_downcast = True
[nvcc]
fastmath = True
flags = –IC:\Anaconda2\libs
compiler_bindir = C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin
base_compiledir = path_to_a_directory_without_such_characters
[blas]
ldflags =
[gcc]
cxxflags = –IC:\Anaconda2\MinGW
数据
面部要害点检测的练习数据集包括7049(96×96)个灰度图画。 关于每个图画,咱们应该学习找到15个要害点的正确方位(x和y坐标),例如
left_eye_center
right_eye_outer_corner
mouth_center_bottom_lip
一个脸部标记出三个要害点的比方。
数据集的一个风趣的改变是,关于一些要害点,咱们只要大约2,000个标签,而其他要害点有7,000多个标签可用于练习。
让咱们编写一些Python代码,从所供给的CSV文件加载数据。 咱们将编写一个能够加载练习和测验数据的函数。 这两个数据集的差异在于测验数据不包括方针值; 这是猜测这些问题的方针。 这儿是咱们的load()函数:
# file kfkd.py
import os
import numpy as np
from pandas.io.parsers import read_csv
from sklearn.utils import shuffle
FTRAIN = ~/data/kaggle-facial-keypoint-detection/training.csv
FTEST = ~/data/kaggle-facial-keypoint-detection/test.csv
def load(test=False, cols=None):
Loads data from FTEST if *test* is True, otherwise from FTRAIN.
Pass a list of *cols* if youre only interested in a subset of the
target columns.
fname = FTEST if test else FTRAIN
df = read_csv(os.path.expanduser(fname)) # load pandas dataframe
# The Image column has pixel values separated by space; convert
# the values to numpy arrays:
df[Image] = df[Image].apply(lambda im: np.fromstring(im, sep= ))
if cols: # get a subset of columns
df = df[list(cols) + [Image]]
print(df.count()) # prints the number of values for each column
df = df.dropna() # drop all rows that have missing values in them
X = np.vstack(df[Image].values) / 255. # scale pixel values to [0, 1]
X = X.astype(np.float32)
if not test: # only FTRAIN has any target columns
y = df[df.columns[:-1]].values
y = (y – 48) / 48 # scale target coordinates to [-1, 1]
X, y = shuffle(X, y, random_state=42) # shuffle train data
y = y.astype(np.float32)
else:
y = None
return X, y
X, y = load()
print(X.shape == {}; X.min == {:.3f}; X.max == {:.3f}.format(
X.shape, X.min(), X.max()))
print(y.shape == {}; y.min == {:.3f}; y.max == {:.3f}.format(
y.shape, y.min(), y.max()))
你没有必要看懂这个函数的每一个细节。 但让咱们看看上面的脚本输出:
$ python kfkd.py
left_eye_center_x 7034
left_eye_center_y 7034
right_eye_center_x 7032
right_eye_center_y 7032
left_eye_inner_corner_x 2266
left_eye_inner_corner_y 2266
left_eye_outer_corner_x 2263
left_eye_outer_corner_y 2263
right_eye_inner_corner_x 2264
right_eye_inner_corner_y 2264
…
mouth_right_corner_x 2267
mouth_right_corner_y 2267
mouth_center_top_lip_x 2272
mouth_center_top_lip_y 2272
mouth_center_bottom_lip_x 7014
mouth_center_bottom_lip_y 7014
Image 7044
dtype: int64
X.shape == (2140, 9216); X.min == 0.000; X.max == 1.000
y.shape == (2140, 30); y.min == -0.920; y.max == 0.996
首要,它打印出了CSV文件中一切列的列表以及每个列的可用值的数量。 因而,尽管咱们有一个图画的练习数据中的一切行,咱们关于mouth_right_corner_x只要个2,267的值等等。
load()回来一个元组(X,y),其间y是方针矩阵。 y的形状是n×m的,其间n是具有一切m个要害点的数据会集的样本数。 删去具有缺失值的一切行是这行代码的功用:
df = df.dropna() # drop all rows that have missing values in them
这个脚本输出的y.shape == (2140, 30)告知咱们,在数据会集只要2140个图画有着一切30个方针值。
一开端,咱们将仅练习这2140个样本。 这使得咱们比样本具有更多的输入巨细(9,216); 过度拟合或许成为一个问题。当然,扔掉70%的练习数据也是一个坏主意。可是现在就这样,咱们将在后边议论。
第一个模型:一个单隐层
现在咱们现已完结了加载数据的作业,让咱们运用Lasagne并创立一个带有一个躲藏层的神经网络。 咱们将从代码开端:
# add to kfkd.py
from lasagne import layers
from lasagne.updates import nesterov_momentum
from nolearn.lasagne import NeuralNet
net1 = NeuralNet(
layers=[ # three layers: one hidden layer
(input, layers.InputLayer),
(hidden, layers.DenseLayer),
(output, layers.DenseLayer),
],
# layer parameters:
input_shape=(None, 9216), # 96×96 input pixels per batch
hidden_num_units=100, # number of units in hidden layer
output_nonlinearity=None, # output layer uses identity function
output_num_units=30, # 30 target values
# optimization method:
update=nesterov_momentum,
update_learning_rate=0.01,
update_momentum=0.9,
regression=True, # flag to indicate were dealing with regression problem
max_epochs=400, # we want to train this many epochs
verbose=1,
)
X, y = load()
net1.fit(X, y)
咱们运用相当多的参数来初始化NeuralNet。让咱们看看他们。首要是三层及其参数:
layers=[ # 三层神经网络:一个隐层
(input, layers.InputLayer),
(hidden, layers.DenseLayer),
(output, layers.DenseLayer),
],
# 层的参数:
input_shape=(None, 9216), # 每个批次96×96个输入样例
hidden_num_units=100, # 隐层中的单元数
output_nonlinearity=None, # 输出用的激活函数
output_num_units=30, # 30个方针值
这儿咱们界说输入层,躲藏层和输出层。在层参数中,咱们命名并指定每个层的类型及其次序。参数input_shape,hidden_??num_units,output_nonlinearity和output_num_units是特定层的参数。它们经过它们的前缀引证层,使得input_shape界说输入层的shape参数,hidden_??num_units界说躲藏层的num_units等等。(看起来有点古怪,咱们有必要指定像这样的参数,但成果是它让咱们关于受运用scikit-learn的管道和参数查找功用具有更好的兼容性。)
咱们将input_shape的第一个维度设置为None。这转换为可变批量巨细。假如你知道批量巨细的话,也能够设置成固定值,假如为None,则是可变值。
咱们将output_nonlinearity设置为None。因而,输出单元的激活仅仅是躲藏层中的激活的线性组合。
DenseLayer运用的默许非线性是rectifier,它其实便是回来max(0, x)。它是当今最受欢迎的激活功用挑选。经过不明确设置hidden_??nonlinearity,咱们挑选rectifier作为咱们躲藏层的激活函数。
神经网络的权重用具有奇妙挑选的距离的均匀散布来初始化。也便是说,Lasagne运用“Glorot-style”初始化来核算出这个距离。
还有几个参数。 一切以update最初的参数用来表明更新方程(或最优化办法)的参数。 更新方程将在每个批次后更新咱们网络的权重。 咱们将运用涅斯捷罗夫动量梯度下降优化办法(nesterov_momentum gradient descent optimization method)来完结这项作业。Lasagne完结的其他办法有许多,如adagrad和rmsprop。咱们挑选nesterov_momentum,由于它现已证明关于很多的问题很好地作业。
”’ optimization method: ””
update=nesterov_momentum,
update_learning_rate=0.01,
update_momentum=0.9,
update_learning_rate界说了梯度下降更新权重的步长。咱们稍后评论学习率和momentum参数,现在的话,这种健全的默许值现已满足了。
上图是不同的最优化办法的比照(animation by?Alec Radford)。星标方位为大局最优值。留意到不增加动量的随机梯度下降是收敛最慢的,咱们在教程中自始至终都是用Nesterov加快过的梯度下降。
在咱们的NeuralNet的界说中,咱们没有指定一个方针函数来完结最小化。这儿运用的还有一个默许值:关于回归问题,它是均方差错(MSE)。
最终一组参数声明咱们正在处理一个回归问题(而不是分类),400是咱们乐意练习的时期数,而且咱们想在练习期间经过设置verbose = 1:
regression=True, # flag to indicate were dealing with regression problem
max_epochs=400, # we want to train this many epochs
verbose=1,
最终两行加载了数据,然后用数据练习了咱们的第一个神经网络。
X, y = load()
net1.fit(X, y)
运转这两行会输出一个表格,每次完结一代就输出一行。每一行里,咱们能够看到当时的练习丢失和验证丢失(最小二乘丢失),以及两者的比率。NeuroNet将会主动把输入数据X分红练习集和测验集,用20%的数据作验证。(比率能够经过参数eval_size=0.2调整)
$ python kfkd.py
…
InputLayer (None, 9216) produces 9216 outputs
DenseLayer (None, 100) produces 100 outputs
DenseLayer (None, 30) produces 30 outputs
Epoch | Train loss | Valid loss | Train / Val
——–|————–|————–|—————-
1 | 0.105418 | 0.031085 | 3.391261
2 | 0.020353 | 0.019294 | 1.054894
3 | 0.016118 | 0.016918 | 0.952734
4 | 0.014187 | 0.015550 | 0.912363
5 | 0.013329 | 0.014791 | 0.901199
…
200 | 0.003250 | 0.004150 | 0.783282
201 | 0.003242 | 0.004141 | 0.782850
202 | 0.003234 | 0.004133 | 0.782305
203 | 0.003225 | 0.004126 | 0.781746
204 | 0.003217 | 0.004118 | 0.781239
205 | 0.003209 | 0.004110 | 0.780738
…
395 | 0.002259 | 0.003269 | 0.690925
396 | 0.002256 | 0.003264 | 0.691164
397 | 0.002254 | 0.003264 | 0.690485
398 | 0.002249 | 0.003259 | 0.690303
399 | 0.002247 | 0.003260 | 0.689252
400 | 0.002244 | 0.003255 | 0.689606
在相对较快的GPU上练习,咱们能够在1分钟之内完结400个epoch的练习。留意测验丢失会一向减小。(假如你练习得满足长时刻,它将会有很小很小的改善)
现在咱们有了一个很好的成果了么?咱们看到测验差错是0.0032,和比赛基准比试一下。记住咱们将方针除以了48以将其缩放到-1到1之间,也便是说,要是想核算均方差错和排行榜的成果比较,有必要把咱们上面得到的0.003255复原到本来的标准。
>>> import numpy as np
>>> np.sqrt(0.003255) * 48
2.7385251505144153
这个值应该能够代表咱们的成果了。当然,这得假定测验调集的数据和练习调集的数据契合相同的散布,但现实却并非如此。
测验网络
咱们刚刚练习的net1目标现已保存了练习时打印在控制台桌面中的记载,咱们能够获取这个记载经过train_history_相关特点,让咱们画出这两个曲线。
train_loss = np.array([i[train_loss] for i in net1.train_history_])
valid_loss = np.array([i[valid_loss] for i in net1.train_history_])
pyplot.plot(train_loss, linewidth=3, label=train)
pyplot.plot(valid_loss, linewidth=3, label=valid)
pyplot.grid()
pyplot.legend()
pyplot.xlabel(epoch)
pyplot.ylabel(loss)
pyplot.ylim(1e-3, 1e-2)
pyplot.yscale(log)
pyplot.show()
咱们能够看到咱们的网络过拟合了,可是成果还不错。现实上,咱们找不到验证过错开端上升的点,所以那种一般用来防止过拟合的early stopping办法在现在还没有什么用途。留意咱们没有选用任何正则化手法,除了挑选节点比较少的隐层——这能够让过拟合保持在可控范围内。
那么网络的猜测成果是什么样的呢?让咱们挑选一些样例来看一看。
def plot_sample(x, y, axis):
img = x.reshape(96, 96)
axis.imshow(img, cmap=gray)
axis.scatter(y[0::2] * 48 + 48, y[1::2] * 48 + 48, marker=x, s=10)
X, _ = load(test=True)
y_pred = net1.predict(X)
fig = pyplot.figure(figsize=(6, 6))
fig.subplots_adjust(
left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
for i in range(16):
ax = fig.add_subplot(4, 4, i + 1, xticks=[], yticks=[])
plot_sample(X[i], y_pred[i], ax)
pyplot.show()
第一个模型猜测的成果(从测验集抽出了16个样例)
猜测成果看起来还不错,可是有点时分仍是有一点偏。让咱们试着做的更好一些。