2018年12月14日星期五

Keras源码分析(4):数据获取

文件:keras/datasets/mnist.py

这个源码可以看作是前篇utils.data_utils.get_file的一个使用案例。

def load_data(path='mnist.npz'):
    path = get_file(path,
        origin='https://s3.amazonaws.com/img-datasets/mnist.npz',
        file_hash='8a61469f7ea1b51cbae51d4f78837e45')
    f = np.load(path)
    x_train, y_train = f['x_train'], f['y_train']
    x_test, y_test = f['x_test'], f['y_test']
    f.close()
    return (x_train, y_train), (x_test, y_test)
在这个load_data函数中,它简单直接地hard code了数据集文件的URL和文件hash校验值作为utils.data_utils.get_file的参数,进行数据集下载,并得到下载后缓存于本地的完整的文件路径path, 然后利用numpy.load载入数据到内存并返回一个dict,其中包含四项:x_train, y_train,x_test, y_test,它们对应的值是numpy数组。

在你的home目录下,你可以找到这个下载的数据集文件,具体路径是:~/.keras/datasets/mnist.npz,它是一个zip压缩文件,打开它你会发现它包含4个numpy数组序列化存储文件:x_train.npy, y_train.npy,x_test.npy, y_test.npy, 这也是为什么np.load返回包含相应4个键值的原因。

MNIST是一个包含60,000个训练图像和10,000测试图像的手写数字的数据集,图像的维度是28x28。下图是取自测试集中的样例:

MNIST sample images.

2018年12月13日星期四

Keras源码分析(3):数据集下载

文件:/keras/utils/data_utils.py

在examples目录下的许多例子都涉及到数据集下载,查看源码你会发现它们最终都是通过keras.utils.data_utils.get_file函数下载的,这是一个普适的数据集下载工具函数,所以有必要了解其功能,以便更好的应用。get_file函数签名如下:

get_file(fname, origin, untar=False, md5_hash=None, file_hash=None, cache_subdir='datasets', hash_algorithm='auto', extract=False, archive_format='auto', cache_dir=None)

fname指的是缓存到本地的文件名,origin其实就是数据集文件的下载地址,即URL,其它参数基本都是不言自明的。函数大致实现如下:

(1)依据cache_dir得到数据文件就存放的文件夹,即:datadir。正常情况下,应该是/.keras/,但如果上述目录不可存取,则是/tmp/.keras/'
if cache_dir is None:
    cache_dir = os.path.join(os.path.expanduser('~'), '.keras')
datadir_base = os.path.expanduser(cache_dir)
if not os.access(datadir_base, os.W_OK):
    datadir_base = os.path.join('/tmp', '.keras')
datadir = os.path.join(datadir_base, cache_subdir)
if not os.path.exists(datadir):
    os.makedirs(datadir)

(2)由datadir和fname得到下载到本地的文件名:fpath
if untar:
    untar_fpath = os.path.join(datadir, fname)
    fpath = untar_fpath + '.tar.gz'else:
    fpath = os.path.join(datadir, fname)


(3)如果文件存在,则不用下载,
download = False
if os.path.exists(fpath):
    ......
else:
    download = True

(4)否则,下载文件
if download:
    print('Downloading data from', origin)
    try:
        try:
            urlretrieve(origin, fpath, dl_progress)
        except HTTPError as e:
            raise Exception(error_msg.format(origin, e.code, e.msg))
        except URLError as e:
            raise Exception(error_msg.format(origin, e.errno, e.reason))
    except (Exception, KeyboardInterrupt):
        if os.path.exists(fpath):
            os.remove(fpath)
            raise

(5)确定是否解压,如需要用extract(untar已过时),最后返回已下载的文件路径
if untar:
    if not os.path.exists(untar_fpath):
        _extract_archive(fpath, datadir, archive_format='tar')
        return untar_fpath

if extract:
    _extract_archive(fpath, datadir, archive_format)
return fpath

2018年12月12日星期三

Keras源码分析(2):入门示例

文件:keras/examples/mnist_cnn.py

在这里我们选择mnist_cnn作为入门示例。这是一个非常简单的深度卷积神经网络,它运行在MNIST数据集上,在12轮训练之后能达到99.25%的精度。其源码及解释如下:

这很简单,没什么好说的
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K

定义模型的超参数: 批大小、标签类别数量和训练轮数。
有些人对这几个概念比较模糊,所以顺便解释一下:批量大小指的是一次训练的样本数目, 它将影响到模型的优化程度和训练速度。当一个完整的数据集通过了神经网络一次并且返回了一次,就称为一个epoch。还有一个概念就是迭代(iteration),迭代是batch需要完成一个epoch的次数。打个比方,一个数据集有2000个训练样本,将这2000个样本分成大小为500的batch,那么完成一个epoch就需要4个iteration。
batch_size = 128
num_classes = 10
epochs = 12

定义输入图像的维度尺寸,要与数据集中图像的大小要一致。
img_rows, img_cols = 28, 28

获取数据集,至于如何获取,将在后面接下来的两节进行分析。mnist.load_data返回的是这样一个形式的元组:(训练集,测试集),(训练集标签,测试集标签)。
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

重新格式化数据集的维度。对于图像文件,有的是chanel在前(channels_first),即:(img_chanels, img_rows, img_cols),有的是chanel在后(channels_last),即 (img_rows, img_cols, img_chanels)。
if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

把数据转换成浮点类型和归一化(Normalization),即将每个数据单元统一映射到[0,1]区间上。如果是有量纲表达式的数据,也要把它们变换为无量纲表达式,成为纯量。经过归一化处理的数据,所有特征都处于同一数量级,可以消除指标之间的量纲和量纲单位的影响,以防止某些特征指标占优。
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

采用one-hot编码处理类别标签,即将一个类别向量转换成二分类矩阵。例如:
    array([0, 2, 1, 2, 0])有3个类别 {0, 1, 2}
    转换后是这个样子:
    array([[ 1.,  0.,  0.],
           [ 0.,  0.,  1.],
           [ 0.,  1.,  0.],
           [ 0.,  0.,  1.],
           [ 1.,  0.,  0.]], dtype=float32)

matricesy_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

以下只做了简单注释,因为每个部分都是重点,所以后续要分单独章节阅读分析。

#new一个Sequential模型 
model = Sequential()  
# 增加层
model.add(Conv2D(32, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

# 配置模型
model.compile(loss=keras.losses.categorical_crossentropy,
              optimizer=keras.optimizers.Adadelta(),
              metrics=['accuracy'])

# 用数据训练模型
model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test))
# 模型评估
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

Keras源码分析(1):包结构概览

参考:github

Keras在github上的源码包结构如下:

|-- docs                      #说明文档
|-- examples                  #应用示例
|-- test                      #测试文件
|-- keras                     #核心源码
      |-- application         #应用实例,如VGG16,RESNET50
      |-- backend             #底层接口,如:tensorflow_backend,theano_backend
      |-- datasets            #数据获取,如boston_housing,mnist
      |-- engine              #网络架构
      |-- layers              #层相关
      |-- legacy              #遗留源码
      |-- preprocessing       #预处理函数
      |-- utils               #实用工具
      |-- wrappers            #scikit-learn封装类
      |-- activations.py      #激活函数
      |-- callbacks.py        #回调函数
      |-- constraints.py      #权重约束,如非零约束等
      |-- initializers.py     #初始化方法
      |-- losses.py           #损失函数
      |-- metrics.py          #度量方法
      |-- models.py           #模型工具
      |-- objectives.py       #目标函数,也就是损失函数,为兼容而保留此文件
      |-- optimizers.py       #优化方法,如SGD,Adam等
      |-- regularizers.py     #正则项,如L1,L2等

我们只重点关注以下目录和文件:

1)examples,它是学习keras入门的好地方,所以我们将从这里着手(见下一节),沿着由易到难,由浅入深的方式一步一步阅读分析。

2)application,如果只是想用keras,这里应该是你学习的第二步,从这里你不仅可以学到如何用keras编程,更重要的是你能学到如何架构深度神经网络。在弄清楚几个经典的深度神经网络应用后,你就可以付诸实践,尝试去解决实际应用中的问题了。

3)engine和layers,是Keras的最核心部分,当然也是要重点阅读分析的部分。

4)activations.py,constraints.py,initializers.py,losses.py,metrics.py,optimizers.py,regularizers.py等直接在keras包下的文件,对于这些文件中的内容,可能是理解原理比分析代码更重要。


需要申明的是:

(1)本“Keras源码分析”系列,只是对Keras的主要代码逻辑进行解析,并不是对所有代码逐行注解,因为这容易陷入或纠缠到一些非常具体的细节,甚至是版本细节,从而导致抓小放大;
(2)本“Keras源码分析”系列,也不是对所有类和函数参数进行解释说明,这方面最好和最完整的文档就是Keras的官方文档。
(3)转载请注明出处和链接。