keras\applications目录文件详解7.1(model.py)-keras学习笔记六

功能:序列模型类和模型相关的实用程序

keras-master\keras\model.py

Keras开发包文件目录

Keras实例文件目录

代码注释

# -*- coding: utf-8 -*-
"""Sequential model class and model-related utilities.
    序列模型类和模型相关的实用程序。
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import warnings
import copy
import json
import os
import yaml
import numpy as np

from . import backend as K
from . import optimizers
from . import layers as layer_module
from .utils.io_utils import ask_to_proceed_with_overwrite
from .utils.generic_utils import has_arg
from .engine.training import Model
from .engine import topology
from .engine.topology import Layer
from .engine.topology import Input
from .engine.topology import InputLayer
from .legacy import layers as legacy_layers
from .legacy import models as legacy_models
from .legacy import interfaces

try:
    import h5py
except ImportError:
    h5py = None


def save_model(model, filepath, overwrite=True, include_optimizer=True):
    """Save a model to a HDF5 file.
       模型保存为HDF5文件
    Note: Please also see
    [How can I install HDF5 or h5py to save my models in Keras?](
        /getting-started/faq/
        #how-can-i-install-HDF5-or-h5py-to-save-my-models-in-Keras)
    in the FAQ for instructions on how to install `h5py`.
    注意:请阅读 [如何在keras中安装HDF5或h5py并保存模型?](
        /getting-started/faq/
        #how-can-i-install-HDF5-or-h5py-to-save-my-models-in-Keras)

    The saved model contains:
        - the model's configuration (topology)
        - the model's weights
        - the model's optimizer's state (if any)
    保存的模型包括:
        - 模型配置文件(拓扑结构)
        - 模型权重
        - 模型优化器状态(如果有的话)

    Thus the saved model can be reinstantiated in
    the exact same state, without any of the code
    used for model definition or training.
    因此,保存的模型可以被重新实例化。完全相同的状态,且没有任何代码用于模型定义或训练。

    # Arguments
    参数
        model: Keras model instance to be saved.
        model:要保存的Keras模型实例
        filepath: one of the following:
            - string, path where to save the model, or
            - h5py.File object where to save the model
        filepath:以下中的一个
            - 字符串,保存模型的路径或
            - h5py文件对象,(用于)保存模型
        overwrite: Whether we should overwrite any existing
            model at the target location, or instead
            ask the user with a manual prompt.
        overwrite:是否应该在目标位置重写任何现有的模型,或者用手动提示询问用户。
        include_optimizer: If True, save optimizer's state together.
        include_optimizer: 如果是true,保存优化器的状态。

    # Raises
    补充:
        ImportError: if h5py is not available.
        ImportError: 如果h5py不可用

    """

    if h5py is None:
        raise ImportError('`save_model` requires h5py.')

    def get_json_type(obj):
        """Serialize any object to a JSON-serializable structure.
        将任何对象序列化为JSON可序列化结构。

        # Arguments
        参数
            obj: the object to serialize
            obj:序列化的对象

        # Returns
        返回
            JSON-serializable structure representing `obj`.
            表示“Obj'”的JSON可串行化结构。

        # Raises
        补充
            TypeError: if `obj` cannot be serialized.
            TypeError:如果`obj` 不能被序列化。
        """
        # if obj is a serializable Keras class instance
        # e.g. optimizer, layer
        # 如果obj是序列化Keas类实例,例如:优化器,层
        if hasattr(obj, 'get_config'):
            return {'class_name': obj.__class__.__name__,
                    'config': obj.get_config()}

        # if obj is any numpy type
        # 如果obj是numpy类型
        if type(obj).__module__ == np.__name__:
            if isinstance(obj, np.ndarray):
                return {'type': type(obj),
                        'value': obj.tolist()}
            else:
                return obj.item()

        # misc functions (e.g. loss function)
        # 混合函数(例如损失函数)
        if callable(obj):
            return obj.__name__

        # if obj is a python 'type'
        # 如果obj是python '类型'
        if type(obj).__name__ == type.__name__:
            return obj.__name__

        raise TypeError('Not JSON Serializable:', obj)

    from . import __version__ as keras_version

    if not isinstance(filepath, h5py.File):
        # If file exists and should not be overwritten.
        # 如果文件存在则不要重写
        if not overwrite and os.path.isfile(filepath):
            proceed = ask_to_proceed_with_overwrite(filepath)
            if not proceed:
                return

        f = h5py.File(filepath, mode='w')
        opened_new_file = True
    else:
        f = filepath
        opened_new_file = False

    try:
        f.attrs['keras_version'] = str(keras_version).encode('utf8')
        f.attrs['backend'] = K.backend().encode('utf8')
        f.attrs['model_config'] = json.dumps({
            'class_name': model.__class__.__name__,
            'config': model.get_config()
        }, default=get_json_type).encode('utf8')

        model_weights_group = f.create_group('model_weights')
        if legacy_models.needs_legacy_support(model):
            model_layers = legacy_models.legacy_sequential_layers(model)
        else:
            model_layers = model.layers
        topology.save_weights_to_hdf5_group(model_weights_group, model_layers)

        if include_optimizer and hasattr(model, 'optimizer'):
            if isinstance(model.optimizer, optimizers.TFOptimizer):
                warnings.warn(
                    'TensorFlow optimizers do not '
                    'make it possible to access '
                    'optimizer attributes or optimizer state '
                    'after instantiation. '
                    'As a result, we cannot save the optimizer '
                    'as part of the model save file.'
                    'You will have to compile your model again '
                    'after loading it. '
                    'Prefer using a Keras optimizer instead '
                    '(see keras.io/optimizers).')
            else:
                f.attrs['training_config'] = json.dumps({
                    'optimizer_config': {
                        'class_name': model.optimizer.__class__.__name__,
                        'config': model.optimizer.get_config()
                    },
                    'loss': model.loss,
                    'metrics': model.metrics,
                    'sample_weight_mode': model.sample_weight_mode,
                    'loss_weights': model.loss_weights,
                }, default=get_json_type).encode('utf8')

                # Save optimizer weights.
                # 保存优化器权重
                symbolic_weights = getattr(model.optimizer, 'weights')
                if symbolic_weights:
                    optimizer_weights_group = f.create_group('optimizer_weights')
                    weight_values = K.batch_get_value(symbolic_weights)
                    weight_names = []
                    for i, (w, val) in enumerate(zip(symbolic_weights,
                                                     weight_values)):
                        # Default values of symbolic_weights is /variable
                        # for Theano and CNTK
                        # heano 和 CNTK符号值的默认值为/变量
                        if K.backend() == 'theano' or K.backend() == 'cntk':
                            if hasattr(w, 'name'):
                                if w.name.split('/')[-1] == 'variable':
                                    name = str(w.name) + '_' + str(i)
                                else:
                                    name = str(w.name)
                            else:
                                name = 'param_' + str(i)
                        else:
                            if hasattr(w, 'name') and w.name:
                                name = str(w.name)
                            else:
                                name = 'param_' + str(i)
                        weight_names.append(name.encode('utf8'))
                    optimizer_weights_group.attrs['weight_names'] = weight_names
                    for name, val in zip(weight_names, weight_values):
                        param_dset = optimizer_weights_group.create_dataset(
                            name,
                            val.shape,
                            dtype=val.dtype)
                        if not val.shape:
                            # scalar 标量
                            param_dset[()] = val
                        else:
                            param_dset[:] = val
        f.flush()
    finally:
        if opened_new_file:
            f.close()


def load_model(filepath, custom_objects=None, compile=True):
    """Loads a model saved via `save_model`.
    加载模型,该模型是通过 `save_model`.(函数)保存的
    # Arguments
    参数
        filepath: one of the following:
            - string, path to the saved model, or
            - h5py.File object from which to load the model
        filepath:以下中的一个
            - 字符串,保存模型的路径或
            - h5py文件对象,(用于)保存模型
        custom_objects: Optional dictionary mapping names
            (strings) to custom classes or functions to be
            considered during deserialization.
        custom_objects:可选的字典映射名称(字符串)到反序列化过程中要考虑的自定义类或函数。
        compile: Boolean, whether to compile the model
            after loading.
        compile: 布尔型,是否编译加载的模型

    # Returns
    返回
        A Keras model instance. If an optimizer was found
        as part of the saved model, the model is already
        compiled. Otherwise, the model is uncompiled and
        a warning will be displayed. When `compile` is set
        to False, the compilation is omitted without any
        warning.
        Keras模型实例,一个KARAS模型实例。如果将优化器作为保存模型的一部分被找到,则该模型已经编译。
        否则,该模型未编译,将显示警告。当“编译”被设置为FALSE时,编译会被忽略而没有任何警告。

    # Raises
    补充
        ImportError: if h5py is not available.
        ImportError: 如何h5py不可用.
        ValueError: In case of an invalid savefile.
        ValueError:如果无效的保存文件.
    """
    if h5py is None:
        raise ImportError('`load_model` requires h5py.')

    if not custom_objects:
        custom_objects = {}

    def convert_custom_objects(obj):
        """Handles custom object lookup.
        处理自定义对象查找。

        # Arguments
        参数
            obj: object, dict, or list.
            obj: 对象,字典或列表.

        # Returns
        返回
            The same structure, where occurrences
                of a custom object name have been replaced
                with the custom object.
            相同的结构,其中自定义对象名称的出现已被自定义对象替换。
        """
        if isinstance(obj, list):
            deserialized = []
            for value in obj:
                deserialized.append(convert_custom_objects(value))
            return deserialized
        if isinstance(obj, dict):
            deserialized = {}
            for key, value in obj.items():
                deserialized[key] = convert_custom_objects(value)
            return deserialized
        if obj in custom_objects:
            return custom_objects[obj]
        return obj

    opened_new_file = not isinstance(filepath, h5py.File)
    if opened_new_file:
        f = h5py.File(filepath, mode='r')
    else:
        f = filepath

    try:
        # instantiate model
        # 实例化模型
        model_config = f.attrs.get('model_config')
        if model_config is None:
            raise ValueError('No model found in config file.')
        model_config = json.loads(model_config.decode('utf-8'))
        model = model_from_config(model_config, custom_objects=custom_objects)

        # set weights
        # 设置权重
        topology.load_weights_from_hdf5_group(f['model_weights'], model.layers)

        # Early return if compilation is not required.
        # 如果不需要编译,则返回。
        if not compile:
            return model

        # instantiate optimizer
        # 实例化优化器
        training_config = f.attrs.get('training_config')
        if training_config is None:
            warnings.warn('No training configuration found in save file: '
                          'the model was *not* compiled. Compile it manually.')
            return model
        training_config = json.loads(training_config.decode('utf-8'))
        optimizer_config = training_config['optimizer_config']
        optimizer = optimizers.deserialize(optimizer_config,
                                           custom_objects=custom_objects)

        # Recover loss functions and metrics.
        # 恢复损失函数和度量(或指标)。
        loss = convert_custom_objects(training_config['loss'])
        metrics = convert_custom_objects(training_config['metrics'])
        sample_weight_mode = training_config['sample_weight_mode']
        loss_weights = training_config['loss_weights']

        # Compile model.
        # 编译模型
        model.compile(optimizer=optimizer,
                      loss=loss,
                      metrics=metrics,
                      loss_weights=loss_weights,
                      sample_weight_mode=sample_weight_mode)

        # Set optimizer weights.
        # 设置优化器权重
        if 'optimizer_weights' in f:
            # Build train function (to get weight updates).
            # 建立训练函数(以获得重量更新)。
            if isinstance(model, Sequential):
                model.model._make_train_function()
            else:
                model._make_train_function()
            optimizer_weights_group = f['optimizer_weights']
            optimizer_weight_names = [n.decode('utf8') for n in
                                      optimizer_weights_group.attrs['weight_names']]
            optimizer_weight_values = [optimizer_weights_group[n] for n in
                                       optimizer_weight_names]
            try:
                model.optimizer.set_weights(optimizer_weight_values)
            except ValueError:
                warnings.warn('Error in loading the saved optimizer '
                              'state. As a result, your model is '
                              'starting with a freshly initialized '
                              'optimizer.')
        return model
    finally:
        if opened_new_file:
            f.close()


def model_from_config(config, custom_objects=None):
    """Instantiates a Keras model from its config.
    从它的配置中实例化一个Keras模型。

    # Arguments
    参数
        config: Configuration dictionary.
        config: 配置字典.
        custom_objects: Optional dictionary mapping names
            (strings) to custom classes or functions to be
            considered during deserialization.
        custom_objects: 可选的字典映射名称(字符串)到反序列化过程中要考虑的自定义类或函数。

    # Returns
    返回
        A Keras model instance (uncompiled).
        一个Keras模型实例(未编译)。

    # Raises
    补充
        TypeError: if `config` is not a dictionary.
        TypeError: 如果'config '不是字典.
    """
    if isinstance(config, list):
        raise TypeError('`model_from_config` expects a dictionary, not a list. '
                        'Maybe you meant to use '
                        '`Sequential.from_config(config)`?')
    return layer_module.deserialize(config, custom_objects=custom_objects)


def model_from_yaml(yaml_string, custom_objects=None):
    """Parses a yaml model configuration file and returns a model instance.
    解析一个yaml模型配置文件并返回一个模型实例。

    # Arguments
    参数
        yaml_string: YAML string encoding a model configuration.
        yaml_string: YAML字符串编码模型的配置。
        custom_objects: Optional dictionary mapping names
            (strings) to custom classes or functions to be
            considered during deserialization.
        custom_objects: 可选的字典映射名称(字符串)到反序列化过程中要考虑的自定义类或函数。

    # Returns
    返回
        A Keras model instance (uncompiled).
        一个Keras模型实例(未编译)。
    """
    config = yaml.load(yaml_string)
    return layer_module.deserialize(config, custom_objects=custom_objects)


def model_from_json(json_string, custom_objects=None):
    """Parses a JSON model configuration file and returns a model instance.
     解析一个JSON模型配置文件并返回一个模型实例。

    # Arguments
    参数
        json_string: JSON string encoding a model configuration.
        json_string: JSON 字符串编码模型的配置。
        custom_objects: Optional dictionary mapping names
            (strings) to custom classes or functions to be
            considered during deserialization.
        custom_objects: 可选的字典映射名称(字符串)到反序列化过程中要考虑的自定义类或函数。
    # Returns
    返回
        A Keras model instance (uncompiled).
         一个Keras模型实例(未编译)。
    """
    config = json.loads(json_string)
    return layer_module.deserialize(config, custom_objects=custom_objects)


class Sequential(Model):
    """Linear stack of layers.
    层的线性叠加。

    # Arguments
    参数
        layers: list of layers to add to the model.
        多个层:要添加到模型中的层的列表。

    # Note
    说明
        The first layer passed to a Sequential model
        should have a defined input shape. What that
        means is that it should have received an `input_shape`
        or `batch_input_shape` argument,
        or for some type of layers (recurrent, Dense...)
        an `input_dim` argument.
        第一层传递到序贯模型
        应该有一个定义的输入形状。(那是什么)
        意思是它应该收到一个“输入形状”。

        或“batch_input_shape”参数,
        或某些类型的层(循环,全连接等)
        或`input_dim` 参数。

    # Example
    例如
        ```python
            model = Sequential()
            # first layer must have a defined input shape
            # 第一层必须有定义的输入形状
            model.add(Dense(32, input_dim=500))
            # afterwards, Keras does automatic shape inference
            # 之后,Keras进行自动形状推理。
            model.add(Dense(32))

            # also possible (equivalent to the above):
            # 也有可能(相当于以上):
            model = Sequential()
            model.add(Dense(32, input_shape=(500,)))
            model.add(Dense(32))

            # also possible (equivalent to the above):
            # 也有可能(相当于以上):
            model = Sequential()
            # here the batch dimension is None,
            # which means any batch size will be accepted by the model.
            # 这里的批次没有度量表示,表示任何批大小都将被模型接受。
            model.add(Dense(32, batch_input_shape=(None, 500)))
            model.add(Dense(32))
        ```
    """

    def __init__(self, layers=None, name=None):
        self.layers = []  # Stack of layers.
        self.model = None  # Internal Model instance.
        self.inputs = []  # List of input tensors
        self.outputs = []  # List of length 1: the output tensor (unique).
        self._trainable = True
        self._initial_weights = None

        # Model attributes.
        # 模型属性
        self._inbound_nodes = []
        self._outbound_nodes = []
        self.built = False

        # Set model name.
        # 设置模型名称
        if not name:
            prefix = 'sequential_'
            name = prefix + str(K.get_uid(prefix))
        self.name = name

        # Add to the model any layers passed to the constructor.
        if layers:
            for layer in layers:
                self.add(layer)

    def add(self, layer):
        """Adds a layer instance on top of the layer stack.
        在层堆栈的顶部添加一个层实例。

        # Arguments
        参数
            layer: layer instance.
            层:层实例

        # Raises
        补充
            TypeError: If `layer` is not a layer instance.
            TypeError: 如果“层”不是一个层实例.
            ValueError: In case the `layer` argument does not
                know its input shape.
            ValueError: 如果“层”参数不知道它的输入形状。
            ValueError: In case the `layer` argument has
                multiple output tensors, or is already connected
                somewhere else (forbidden in `Sequential` models).
            ValueError: 如果`layer`参数有多个输出张量,或者已经连接到其他地方(在 `Sequential`模型中是禁止的)。
        """
        if not isinstance(layer, Layer):
            raise TypeError('The added layer must be '
                            'an instance of class Layer. '
                            'Found: ' + str(layer))
        if not self.outputs:
            # First layer in model: check that it is an input layer.
            # 模型中的第一层:检查它是一个输入层。
            if not isinstance(layer, (InputLayer, legacy_layers.Merge)):
                # Create an input layer.
                # 建立一个输入层
                # First, we need to infer its expected input shape and dtype.
                # 首先,我们需要推断它的预期输入形状和类型。
                if isinstance(layer, (Model, Sequential)):
                    # We were passed a model as first layer.
                    # 我们通过一个模型作为第一层。
                    # This requires a specific way to figure out the
                    # input shape and dtype.
                    # 这需要一种特定的方法来确定输入形状和类型。
                    if not layer.layers:
                        raise ValueError('Cannot add an empty model '
                                         'to a `Sequential` model.')
                    # In case of nested models: recover the first layer
                    # of the deepest model to infer input shape and dtype.
                    # 在嵌套模型的情况下:恢复最深层模型的第一层以推断输入形状和类型。
                    first_layer = layer.layers[0]
                    while isinstance(first_layer, (Model, Sequential)):
                        first_layer = first_layer.layers[0]
                    batch_shape = first_layer.batch_input_shape
                    dtype = first_layer.dtype
                else:
                    # We were passed a regular layer, and it should
                    # know about its input shape. Otherwise, that's an error.
                    # 我们通过了一个规则层,知道它的输入形状。否则,这是一个错误。
                    if not hasattr(layer, 'batch_input_shape'):
                        raise ValueError('The first layer in a '
                                         'Sequential model must '
                                         'get an `input_shape` or '
                                         '`batch_input_shape` argument.')
                    batch_shape = layer.batch_input_shape
                    dtype = layer.dtype
                # Instantiate the input layer.
                # 实例化输入层
                x = Input(batch_shape=batch_shape,
                          dtype=dtype,
                          name=layer.name + '_input')
                # This will build the current layer
                # and create the node connecting the current layer
                # to the input layer we just created.
                # 建立当前层并创建连接当前层到刚刚创建的输入层的节点。
                layer(x)

            if len(layer._inbound_nodes[-1].output_tensors) != 1:
                raise ValueError('All layers in a Sequential model '
                                 'should have a single output tensor. '
                                 'For multi-output layers, '
                                 'use the functional API.')

            self.outputs = [layer._inbound_nodes[-1].output_tensors[0]]
            self.inputs = topology.get_source_inputs(self.outputs[0])

            # We create an input node, which we will keep updated
            # as we add more layers
            # 创建一个输入节点,当添加更多的层时,将不断更新它。
            topology.Node(outbound_layer=self,
                          inbound_layers=[],
                          node_indices=[],
                          tensor_indices=[],
                          input_tensors=self.inputs,
                          output_tensors=self.outputs,
                          # no model-level masking for now
                          # 现在没有模型级掩码
                          input_masks=[None for _ in self.inputs],
                          output_masks=[None],
                          input_shapes=[x._keras_shape for x in self.inputs],
                          output_shapes=[self.outputs[0]._keras_shape])
        else:
            output_tensor = layer(self.outputs[0])
            if isinstance(output_tensor, list):
                raise TypeError('All layers in a Sequential model '
                                'should have a single output tensor. '
                                'For multi-output layers, '
                                'use the functional API.')
            self.outputs = [output_tensor]
            # update self._inbound_nodes
            # 更新self._inbound_nodes
            self._inbound_nodes[0].output_tensors = self.outputs
            self._inbound_nodes[0].output_shapes = [self.outputs[0]._keras_shape]

        self.layers.append(layer)
        self.built = False

    def pop(self):
        """Removes the last layer in the model.
        移除模型中的最后一层。

        # Raises
        补充
            TypeError: if there are no layers in the model.
            TypeError: 如果模型中没有层。
        """
        if not self.layers:
            raise TypeError('There are no layers in the model.')

        self.layers.pop()
        if not self.layers:
            self.outputs = []
            self._inbound_nodes = []
            self._outbound_nodes = []
        else:
            self.layers[-1]._outbound_nodes = []
            self.outputs = [self.layers[-1].output]
            # update self._inbound_nodes
            # 更新 self._inbound_nodes
            self._inbound_nodes[0].output_tensors = self.outputs
            self._inbound_nodes[0].output_shapes = [self.outputs[0]._keras_shape]
        self.built = False

    def get_layer(self, name=None, index=None):
        """Retrieve a layer that is part of the model.
        # 检索作为模型的一部分的层。

        Returns a layer based on either its name (unique)
        or its index in the graph. Indices are based on
        order of horizontal graph traversal (bottom-up).
        在图中返回一个基于其名称(唯一)或其索引的层。索引是基于水平图遍历的顺序(自底向上)。

        # Arguments
        参数
            name: string, name of layer.
            name: 字符串,层的名字
            index: integer, index of layer.
            index: 整型,层索引

        # Returns
        返回
            A layer instance.
            层实例
        """
        if not self.built:
            self.build()
        return self.model.get_layer(name, index)

    def call(self, inputs, mask=None):
        if not self.built:
            self.build()
        return self.model.call(inputs, mask)

    def build(self, input_shape=None):
        if not self.inputs or not self.outputs:
            raise TypeError('Sequential model cannot be built: model is empty.'
                            ' Add some layers first.')
        # actually create the model
        # 实际创建模型
        self.model = Model(self.inputs, self.outputs[0],
                           name=self.name + '_model')
        self.model.trainable = self.trainable

        # mirror model attributes
        # 镜像模型属性
        self.supports_masking = self.model.supports_masking
        self._output_mask_cache = self.model._output_mask_cache
        self._output_tensor_cache = self.model._output_tensor_cache
        self._output_shape_cache = self.model._output_shape_cache
        self.input_layers = self.model.input_layers
        self.input_layers_node_indices = self.model.input_layers_node_indices
        self.input_layers_tensor_indices = self.model.input_layers_tensor_indices
        self.output_layers = self.model.output_layers
        self.output_layers_node_indices = self.model.output_layers_node_indices
        self.output_layers_tensor_indices = self.model.output_layers_tensor_indices
        self._nodes_by_depth = self.model._nodes_by_depth
        self.output_names = self.model.output_names
        self.input_names = self.model.input_names
        self._feed_input_names = self.model._feed_input_names
        self._feed_inputs = self.model._feed_inputs
        self._container_nodes = self.model._container_nodes

        # Make sure child model callbacks
        # will call the parent Sequential model.
        # 确保子模型回调将调用父序列模型。
        self.model.callback_model = self
        self.built = True

    @property
    def uses_learning_phase(self):
        if not self.built:
            self.build()
        return self.model.uses_learning_phase

    @property
    def _flattened_layers(self):
        layers = []
        if self.layers:
            # Support for legacy models
            # 支持遗留模型
            if isinstance(self.layers[0], legacy_layers.Merge):
                merge = self.layers[0]
                for layer in merge.layers:
                    if hasattr(layer, '_flattened_layers'):
                        for sublayer in layer._flattened_layers:
                            if sublayer not in layers:
                                layers.append(sublayer)
                    elif hasattr(layer, 'layers'):
                        for sublayer in layer.layers:
                            if sublayer not in layers:
                                layers.append(sublayer)
                    else:
                        if layer not in layers:
                            layers.append(layer)
            else:
                if self.layers[0] not in layers:
                    layers.append(self.layers[0])
            for layer in self.layers[1:]:
                if layer not in layers:
                    layers.append(layer)
        return layers

    def _gather_list_attr(self, attr):
        all_attrs = []
        for layer in self._flattened_layers:
            all_attrs += getattr(layer, attr, [])
        return all_attrs

    @property
    def trainable(self):
        return self._trainable

    @trainable.setter
    def trainable(self, value):
        if self.built:
            self.model.trainable = value
        self._trainable = value

    @property
    def trainable_weights(self):
        if not self.trainable:
            return []
        # Support for legacy behavior
        # 支持遗留行为
        return self._gather_list_attr('trainable_weights')

    @property
    def non_trainable_weights(self):
        # Support for legacy behavior
        # 支持遗留行为
        weights = self._gather_list_attr('non_trainable_weights')
        if not self.trainable:
            trainable_weights = self._gather_list_attr('trainable_weights')
            return trainable_weights + weights
        return weights

    @property
    def updates(self):
        if not self.built:
            self.build()
        return self.model.updates

    @property
    def state_updates(self):
        if not self.built:
            self.build()
        return self.model.state_updates

    def get_updates_for(self, inputs):
        if not self.built:
            self.build()
        return self.model.get_updates_for(inputs)

    @property
    def losses(self):
        if not self.built:
            self.build()
        return self.model.losses

    def get_losses_for(self, inputs):
        if not self.built:
            self.build()
        return self.model.get_losses_for(inputs)

    @property
    def regularizers(self):
        if not self.built:
            self.build()
        return self.model.regularizers

    def get_weights(self):
        """Retrieves the weights of the model.
        恢复模型权重

        # Returns
        返回
            A flat list of Numpy arrays
            (one array per model weight).
            # Numpy数组的扁平列表(每个模型权重的一个数组)。

        """
        # Legacy support
        # 遗产支持
        if legacy_models.needs_legacy_support(self):
            layers = legacy_models.legacy_sequential_layers(self)
            weights = []
            for layer in layers:
                weights.append(layer.get_weights())
            return weights

        if not self.built:
            self.build()
        return self.model.get_weights()

    def set_weights(self, weights):
        """Sets the weights of the model.
        设置模型的权重。

        # Arguments
        参数
            weights: Should be a list
                of Numpy arrays with shapes and types matching
                the output of `model.get_weights()`.
            weights:应该是一个形状与类型匹配的numpy数组的列表,与`model.get_weights()的输出匹配。
        """
        # Legacy support
        if legacy_models.needs_legacy_support(self):
            layers = legacy_models.legacy_sequential_layers(self)
            for layer in layers:
                nb_param = len(layer.weights)
                layer.set_weights(weights[:nb_param])
                weights = weights[nb_param:]

        if not self.built:
            self.build()
        self.model.set_weights(weights)

    def load_weights(self, filepath, by_name=False, skip_mismatch=False, reshape=False):
        if h5py is None:
            raise ImportError('`load_weights` requires h5py.')
        with h5py.File(filepath, mode='r') as f:
            if 'layer_names' not in f.attrs and 'model_weights' in f:
                f = f['model_weights']

            # Legacy support
            if legacy_models.needs_legacy_support(self):
                layers = legacy_models.legacy_sequential_layers(self)
            else:
                layers = self.layers
            if by_name:
                topology.load_weights_from_hdf5_group_by_name(f, layers,
                                                              skip_mismatch=skip_mismatch,
                                                              reshape=reshape)
            else:
                topology.load_weights_from_hdf5_group(f, layers, reshape=reshape)

    def save_weights(self, filepath, overwrite=True):
        """Saves the weights of a model.
        保存模型权重

        Note: Please also see
        [How can I install HDF5 or h5py to save my models in Keras?](
        /getting-started/faq/
        #how-can-i-install-HDF5-or-h5py-to-save-my-models-in-Keras)
        in the FAQ for instructions on how to install `h5py`.
        注意:请阅读 [如何在keras中安装HDF5或h5py并保存模型?](
        /getting-started/faq/
        #how-can-i-install-HDF5-or-h5py-to-save-my-models-in-Keras)
        在FAQ(全写为frequently asked questions,常见问题解答)中关于如何安装“H5PY”的说明。
        """

        if h5py is None:
            raise ImportError('`save_weights` requires h5py.')
        # If file exists and should not be overwritten:
        # 如果文件存在不要重写
        if not overwrite and os.path.isfile(filepath):
            proceed = ask_to_proceed_with_overwrite(filepath)
            if not proceed:
                return
        # Legacy support
        # 遗留支持
        if legacy_models.needs_legacy_support(self):
            layers = legacy_models.legacy_sequential_layers(self)
        else:
            layers = self.layers

        with h5py.File(filepath, 'w') as f:
            topology.save_weights_to_hdf5_group(f, layers)
            f.flush()

    def compile(self, optimizer, loss,
                metrics=None,
                sample_weight_mode=None,
                weighted_metrics=None,
                target_tensors=None,
                **kwargs):
        """Configures the model for training.
        为训练配置模型

        # Arguments
        参数
            optimizer: String (name of optimizer) or optimizer object.
                See [optimizers](/optimizers).
            optimizer: 字符串(优化器名称)或优化器对象。参见 [optimizers](/optimizers)。
            loss: String (name of objective function) or objective function.
                See [losses](/losses).
                If the model has multiple outputs, you can use a different loss
                on each output by passing a dictionary or a list of losses.
                The loss value that will be minimized by the model
                will then be the sum of all individual losses.
            loss:字符串(目标函数名)或目标函数。
                 参见[losses](/losses).
                 如果模型具有多个输出,则可以通过传递字典或损失列表来在每个输出上使用不同的损失。
                 模型将最小化的损失值将是所有单个损失的总和
            metrics: List of metrics to be evaluated by the model
                during training and testing.
                Typically you will use `metrics=['accuracy']`.
                To specify different metrics for different outputs of a
                multi-output model, you could also pass a dictionary,
                such as `metrics={'output_a': 'accuracy'}`.
            metrics:在训练和测试期间由模型评估的度量表。
                通常你会使用`metrics=['accuracy']`.
                为了为多输出模型的不同输出指定不同的度量,还可以传递字典,例如`metrics={'output_a': 'accuracy'}`。
            sample_weight_mode: If you need to do timestep-wise
                sample weighting (2D weights), set this to `"temporal"`.
                `None` defaults to sample-wise weights (1D).
                If the model has multiple outputs, you can use a different
                `sample_weight_mode` on each output by passing a
                dictionary or a list of modes.
            sample_weight_mode:如果需要做时间步长的样本加权(2D权重),将其设置为`"temporal"`。“没有”默认为样本权重(1D)。
                如果模型有多个输出,可以通过传递字典或模式列表对每个输出使用不同的“sample_weight_mode”。
            weighted_metrics: List of metrics to be evaluated and weighted
                by sample_weight or class_weight during training and testing.
            weighted_metrics:在训练和测试期间要用sample_weight或class_weight来衡量和衡量的度量表。
            target_tensors: By default, Keras will create a placeholder for the
                model's target, which will be fed with the target data during
                training. If instead you would like to use your own
                target tensor (in turn, Keras will not expect external
                Numpy data for these targets at training time), you
                can specify them via the `target_tensors` argument.
                It should be a single tensor
                (for a single-output `Sequential` model).
            target_tensors:默认情况下,Keras将为模型的目标创建占位符,在训练期间,目标将被提供给目标数据。如果您想
            使用自己的目标张量(反过来,Keras在训练时不会期望这些目标的外部Numpy数据),那么可以通过“target_tensors”参
            数指定它们。它应该是一个单张量(对于单输出的‘Sequential’模型)。
            **kwargs: When using the Theano/CNTK backends, these arguments
                are passed into `K.function`.
                When using the TensorFlow backend,
                these arguments are passed into `tf.Session.run`.
            **kwargs: 如果使用Theano/CNTK后台()后端,这些参数被传递到`K.function`中。当使用TensorFlow后台时,这些参数
            被传递到 `tf.Session.run`中。

        # Raises
        补充
            ValueError: In case of invalid arguments for
                `optimizer`, `loss`, `metrics` or `sample_weight_mode`.
            ValueError: 无效参数的情况:`optimizer`, `loss`, `metrics` or `sample_weight_mode`.

        # Example
        例如
            ```python
                model = Sequential()
                model.add(Dense(32, input_shape=(500,)))
                model.add(Dense(10, activation='softmax'))
                model.compile(optimizer='rmsprop',
                              loss='categorical_crossentropy',
                              metrics=['accuracy'])
            ```
        """
        # create the underlying model
        # 建立底层模型
        self.build()
        # call compile method of Model class
        # 模型类的调用编译方法
        self.model.compile(optimizer, loss,
                           metrics=metrics,
                           sample_weight_mode=sample_weight_mode,
                           weighted_metrics=weighted_metrics,
                           target_tensors=target_tensors,
                           **kwargs)
        self.optimizer = self.model.optimizer
        self.loss = self.model.loss
        self.metrics = self.model.metrics
        self.loss_weights = self.model.loss_weights
        self.sample_weight_mode = self.model.sample_weight_mode
        self.weighted_metrics = self.model.weighted_metrics
        self.targets = self.model.targets
        self.metrics_tensors = self.model.metrics_tensors
        self.metrics_names = self.model.metrics_names
        self.sample_weights = self.model.sample_weights
        self.total_loss = self.model.total_loss

    def fit(self,
            x=None,
            y=None,
            batch_size=None,
            epochs=1,
            verbose=1,
            callbacks=None,
            validation_split=0.,
            validation_data=None,
            shuffle=True,
            class_weight=None,
            sample_weight=None,
            initial_epoch=0,
            steps_per_epoch=None,
            validation_steps=None,
            **kwargs):
        """Trains the model for a fixed number of epochs (iterations on a dataset).
         为固定数量的周期(基于数据集的迭代)训练模型。(数据集训练一遍为一个epochs)

        # Arguments
        参数
            x: Numpy array of training data.
                If the input layer in the model is named, you can also pass a
                dictionary mapping the input name to a Numpy array.
                `x` can be `None` (default) if feeding from
                framework-native tensors (e.g. TensorFlow data tensors).
            x: 训练数据的Numpy数组。
               如果模型中的输入层被命名,您也可以通过一个字典将输入名称映射到一个Numpy数组。
               “x”可以是“无”(默认值),如果从框架本征张量进给(例如:TensorFlow数据张量)
            y: Numpy array of target (label) data.
                If the output layer in the model is named, you can also pass a
                dictionary mapping the output name to a Numpy array.
                `y` can be `None` (default) if feeding from
                framework-native tensors (e.g. TensorFlow data tensors).
            y: 目标(标签)数据的Numpy数组。
                如果模型中的输入层被命名,您也可以通过一个字典将输入名称映射到一个Numpy数组。
               “y”可以是“无”(默认值),如果从框架本征张量进给(例如:TensorFlow数据张量)
            batch_size: Integer or `None`.
                Number of samples per gradient update.
                If unspecified, it will default to 32.
            batch_size: 整型或无。
                每个梯度更新的样本数。
                如果未指定,它将默认为32。
            epochs: Integer. Number of epochs to train the model.
                An epoch is an iteration over the entire `x` and `y`
                data provided.
                Note that in conjunction with `initial_epoch`,
                `epochs` is to be understood as "final epoch".
                The model is not trained for a number of iterations
                given by `epochs`, but merely until the epoch
                of index `epochs` is reached.
            epochs: 整型。训练模型的周期数量。
                一个周期是一个覆盖已提供的数据集整个`x` 和 `y`的迭代。
                注意,与`initial_epoch`一起的`epochs` 应被理解为"final epoch"。
                该模型不被训练用于“epochs”给出的多次
                迭代,而仅直到达到索引“epochs”的周期为止。
            verbose: 0, 1, or 2. Verbosity mode.
                0 = silent, 1 = progress bar, 2 = one line per epoch.
            verbose: 0, 1, 或 2.冗长模式
                0 = 沉默, 1 = 进度条, 2 = 每个周期一行.
            callbacks: List of `keras.callbacks.Callback` instances.
                List of callbacks to apply during training.
                See [callbacks](/callbacks).
            callback: `keras.callbacks.Callback`实例列表
                在训练过程中应用的回调列表。
                参见[callbacks](/callbacks).
            validation_split: Float between 0 and 1.
                Fraction of the training data to be used as validation data.
                The model will set apart this fraction of the training data,
                will not train on it, and will evaluate
                the loss and any model metrics
                on this data at the end of each epoch.
                The validation data is selected from the last samples
                in the `x` and `y` data provided, before shuffling.
            validation_split: Float基于0 和 1.
                作为验证数据的训练数据的分数(分割比率)。
                该模型将把训练数据的这一部分分开,不对其进行训练,并在每个阶段结束时评估该数据的损失和任何模型度量。
                验证数据是从“X”和“Y”数据中的最后一个样本中选择的,然后再进行洗牌。
            validation_data: tuple `(x_val, y_val)` or tuple
                `(x_val, y_val, val_sample_weights)` on which to evaluate
                the loss and any model metrics at the end of each epoch.
                The model will not be trained on this data.
                This will override `validation_split`.
            validation_data: `(x_val, y_val)`元组或`(x_val, y_val, val_sample_weights)`元组,基于它在每个周期结束
                评估损失和模型度量。
                模型没有在该数据集训练。
                重写 `validation_split`.。
            shuffle: Boolean (whether to shuffle the training data
                before each epoch) or str (for 'batch').
                'batch' is a special option for dealing with the
                limitations of HDF5 data; it shuffles in batch-sized chunks.
                Has no effect when `steps_per_epoch` is not `None`.
             shuffle:布尔型(是否在每个周期前洗牌训练数据)或str(for 'batch')
                    “batch”是处理HDF5数据限制的一个特殊选项;它以批量大小的块进行洗牌。
                    当“step_per_epoch”不是“None”时没有效果。
            class_weight: Optional dictionary mapping class indices (integers)
                to a weight (float) value, used for weighting the loss function
                (during training only).
                This can be useful to tell the model to
                "pay more attention" to samples from
                an under-represented class.
            class_weight:可选的字典将类索引(整数)映射到权重(浮动)值,用于对损失函数进行加权(仅在训练期间)。
                这有助于告诉模型“更加关注”欠代表性的类样本。
            sample_weight: Optional Numpy array of weights for
                the training samples, used for weighting the loss function
                (during training only). You can either pass a flat (1D)
                Numpy array with the same length as the input samples
                (1:1 mapping between weights and samples),
                or in the case of temporal data,
                you can pass a 2D array with shape
                `(samples, sequence_length)`,
                to apply a different weight to every timestep of every sample.
                In this case you should make sure to specify
                `sample_weight_mode="temporal"` in `compile()`.
            sample_weight:用于训练样本的权重的可选的Numpy数组,用于加权损失函数(仅在训练期间)。
            可以传递与输入样本相同长度的平面(1D)Numpy数组(权重和样本之间的1:1映射),或者,对于时态数据,可以传递
            一个具有形状  `(samples, sequence_length)`的2D数组,以对每个采样的每个时间步施加不同的权重。在这种情况下,
            应该在编译函数`compile()`确保指定`sample_weight_mode="temporal"`。
            initial_epoch: Epoch at which to start training
                (useful for resuming a previous training run).
            initial_epoch: (开始训练的)周期
                (有助于恢复以前的训练运行)
            steps_per_epoch: Total number of steps (batches of samples)
                before declaring one epoch finished and starting the
                next epoch. When training with input tensors such as
                TensorFlow data tensors, the default `None` is equal to
                the number of samples in your dataset divided by
                the batch size, or 1 if that cannot be determined.
            steps_per_epoch:在声明一个周期完成并开始下一个周期之前的步骤总数(批样本)。当使用输入
                张量(如TensorFlow数据张量)进行训练时,默认的“None”等于数据集中的样本数除以批处理(每个批次的样本
                个数)大小,如果不能确定,则为1。
            validation_steps: Only relevant if `steps_per_epoch`
                is specified. Total number of steps (batches of samples)
                to validate before stopping.
            validation_steps:仅指定相关的`steps_per_epoch`。在停止之前验证步骤(批次样本)的总数。

        # Returns
        返回
            A `History` object. Its `History.history` attribute is
            a record of training loss values and metrics values
            at successive epochs, as well as validation loss values
            and validation metrics values (if applicable).
            一个 `History`对象。它的 `History.history`属性是连续时期训练损失值和度量值的记录,以及验证损失值和验证
            度量值(如果适用的话)。


        # Raises
        补充
            RuntimeError: If the model was never compiled.
            RuntimeError: 如果模型没有编译
            ValueError: In case of mismatch between the provided input data
                and what the model expects.
            ValueError: 在所提供的输入数据与模型所期望的不匹配的情况下。
        """
        # Legacy support
        # 遗产支持
        if 'nb_epoch' in kwargs:
            warnings.warn('The `nb_epoch` argument in `fit` '
                          'has been renamed `epochs`.')
            epochs = kwargs.pop('nb_epoch')
        if kwargs:
            raise TypeError('Unrecognized keyword arguments: ' + str(kwargs))

        if not self.built:
            raise RuntimeError('The model needs to be compiled '
                               'before being used.')
        return self.model.fit(x, y,
                              batch_size=batch_size,
                              epochs=epochs,
                              verbose=verbose,
                              callbacks=callbacks,
                              validation_split=validation_split,
                              validation_data=validation_data,
                              shuffle=shuffle,
                              class_weight=class_weight,
                              sample_weight=sample_weight,
                              initial_epoch=initial_epoch,
                              steps_per_epoch=steps_per_epoch,
                              validation_steps=validation_steps)

    def evaluate(self, x=None, y=None,
                 batch_size=None,
                 verbose=1,
                 sample_weight=None,
                 steps=None):
        """ """
        if not self.built:
            raise RuntimeError('The model needs to be compiled '
                               'before being used.')
        return self.model.evaluate(x, y,
                                   batch_size=batch_size,
                                   verbose=verbose,
                                   sample_weight=sample_weight,
                                   steps=steps)

    def predict(self, x, batch_size=None, verbose=0, steps=None):
        """Generates output predictions for the input samples.
            为输入样本生成输出预测

        The input samples are processed batch by batch.
        输入样本朱逐批次输处理

        # Arguments
        参数
            x: the input data, as a Numpy array.
            x:输入数据,Numpy数组
            batch_size: Integer. If unspecified, it will default to 32.
            batch_size: 整数。如果未指定,它将默认为32。
            verbose: verbosity mode, 0 or 1.
            verbose: 冗余模式, 0 或 1.
            steps: Total number of steps (batches of samples)
                before declaring the prediction round finished.
                Ignored with the default value of `None`.
            steps: 声明预测回合结束之前的步骤总数(批样本)。忽略`None`的默认值。

        # Returns
        返回
            A Numpy array of predictions.
            预测的Numpy数组
        """
        if not self.built:
            self.build()
        return self.model.predict(x, batch_size=batch_size, verbose=verbose,
                                  steps=steps)

    def predict_on_batch(self, x):
        """Returns predictions for a single batch of samples.
        返回单批样本的预测。

        # Arguments
        参数
            x: input data, as a Numpy array or list of Numpy arrays
                (if the model has multiple inputs).
            x: 输入数据,Numpy数组或Numpy数组列表
                (如果模型有多个输入).

        # Returns
        返回
            A Numpy array of predictions.
            预测的Numpy数组
        """
        if not self.built:
            self.build()
        return self.model.predict_on_batch(x)

    def train_on_batch(self, x, y, class_weight=None,
                       sample_weight=None):
        """Single gradient update over one batch of samples.
            单个梯度更新覆盖一批样本。
        # Arguments
        参数
            x: input data, as a Numpy array or list of Numpy arrays
                (if the model has multiple inputs).
            x: 输入数据,Numpy数组或Numpy数组列表
                (如果模型有多个输入).
            y: labels, as a Numpy array.
            y:标签,Numpy数组
            class_weight: dictionary mapping classes to a weight value,
                used for scaling the loss function (during training only).
            class_weight: 字典映射类到权重值,用于缩放损失函数(仅在训练期间)。
            sample_weight: sample weights, as a Numpy array.
            sample_weight: .样本权重,Numpy数组

        # Returns
        返回
            Scalar training loss (if the model has no metrics)
            or list of scalars (if the model computes other metrics).
            The attribute `model.metrics_names` will give you
            the display labels for the scalar outputs.
            标量训练损失(如果模型没有度量)或标量列表(如果模型计算其他度量)。属性`model.metrics_names` 将为标量输出
            提供显示标签。

        # Raises
        补充
            RuntimeError: if the model was never compiled.
            RuntimeError: 如果模型没有编译.
        """
        if not self.built:
            raise RuntimeError('The model needs to be compiled '
                               'before being used.')
        return self.model.train_on_batch(x, y,
                                         sample_weight=sample_weight,
                                         class_weight=class_weight)

    def test_on_batch(self, x, y,
                      sample_weight=None):
        """Evaluates the model over a single batch of samples.
        在一批样品上评估模型。

        # Arguments
        参数
            x: input data, as a Numpy array or list of Numpy arrays
                (if the model has multiple inputs).
            x: 输入数据,Numpy数组或Numpy数组列表
                (如果模型有多个输入).
            y: labels, as a Numpy array.
            y: 标签, Numpy数组.
            sample_weight: sample weights, as a Numpy array.
            sample_weight: 样本权重,Numpy数组.

        # Returns
        返回
            Scalar test loss (if the model has no metrics)
            or list of scalars (if the model computes other metrics).
            The attribute `model.metrics_names` will give you
            the display labels for the scalar outputs.
            标量测试损失(如果模型没有度量)或标量列表(如果模型计算其他度量)。属性`model.metrics_names` 将为标量输出
            提供显示标签。

        # Raises
        返回
            RuntimeError: if the model was never compiled.
            RuntimeError:如果模型没有编译.
        """
        if not self.built:
            raise RuntimeError('The model needs to be compiled '
                               'before being used.')
        return self.model.test_on_batch(x, y,
                                        sample_weight=sample_weight)

    def predict_proba(self, x, batch_size=None, verbose=0, steps=None):
        """Generates class probability predictions for the input samples.
        生成输入样本的类概率预测。

        The input samples are processed batch by batch.
        输入样本逐批处理。

        # Arguments
        参数
            x: input data, as a Numpy array or list of Numpy arrays
                (if the model has multiple inputs).
            x: 输入数据,Numpy数组或Numpy数组列表
                (如果模型有多个输入).
            batch_size: Integer. If unspecified, it will default to 32.
            batch_size: 整数。如果未指定,它将默认为32。
            verbose: verbosity mode, 0 or 1.
            verbose: 冗余模式, 0 或 1.
            steps: Total number of steps (batches of samples)
                before declaring the prediction round finished.
                Ignored with the default value of `None`.
            steps: 声明预测回合结束之前的步骤总数(批样本)。忽略“无”的默认值。


        # Returns
        返回
            A Numpy array of probability predictions.
            概率预测的Numpy数组。
        """
        preds = self.predict(x, batch_size, verbose, steps=steps)
        if preds.min() < 0. or preds.max() > 1.:
            warnings.warn('Network returning invalid probability values. '
                          'The last layer might not normalize predictions '
                          'into probabilities '
                          '(like softmax or sigmoid would).')
        return preds

    def predict_classes(self, x, batch_size=None, verbose=0, steps=None):
        """Generate class predictions for the input samples.
        生成输入样本的类预测。

        The input samples are processed batch by batch.
        输入样本逐批处理。

        # Arguments
        参数
            x: input data, as a Numpy array or list of Numpy arrays
                (if the model has multiple inputs).
            x: 输入数据,Numpy数组或Numpy数组列表
                (如果模型有多个输入).
            batch_size: Integer. If unspecified, it will default to 32.
            batch_size: 整数。如果未指定,它将默认为32。
            verbose: verbosity mode, 0 or 1.
            verbose: 冗余模式, 0 或 1.
            steps: Total number of steps (batches of samples)
                before declaring the prediction round finished.
                Ignored with the default value of `None`.
            steps: 声明预测回合结束之前的步骤总数(批样本)。忽略“无”的默认值。

        # Returns
        返回
            A numpy array of class predictions.
            概率预测的Numpy数组。
        """
        proba = self.predict(x, batch_size=batch_size, verbose=verbose,
                             steps=steps)
        if proba.shape[-1] > 1:
            return proba.argmax(axis=-1)
        else:
            return (proba > 0.5).astype('int32')

    @interfaces.legacy_generator_methods_support
    def fit_generator(self,
                      generator,
                      steps_per_epoch=None,
                      epochs=1,
                      verbose=1,
                      callbacks=None,
                      validation_data=None,
                      validation_steps=None,
                      class_weight=None,
                      max_queue_size=10,
                      workers=1,
                      use_multiprocessing=False,
                      shuffle=True,
                      initial_epoch=0):
        """Fits the model on data generated batch-by-batch by a Python generator.
        通过Python生成器将模型按批生成的数据拟合。

        The generator is run in parallel to the model, for efficiency.
        For instance, this allows you to do real-time data augmentation
        on images on CPU in parallel to training your model on GPU.
        为了效率,生成器与模型并行运行。例如,这允许您进行实时数据增强。在CPU上的图像并行地在GPU上训练您的模型。

        The use of `keras.utils.Sequence` guarantees the ordering
        and guarantees the single use of every input per epoch when
        using `use_multiprocessing=True`.
        使用`keras.utils.Sequence'保证了排序,并且保证了在使用`use_multiprocessing=True`时每个周期每个输入的单次使用。

        # Arguments
        参数
            generator: A generator or an instance of `Sequence`
                (`keras.utils.Sequence`) object in order to avoid duplicate data
                    when using multiprocessing.
                The output of the generator must be either
                - a tuple `(inputs, targets)`
                - a tuple `(inputs, targets, sample_weights)`.
                This tuple (a single output of the generator) makes a single
                batch. Therefore, all arrays in this tuple must have the same
                length (equal to the size of this batch). Different batches may
                have different sizes. For example, the last batch of the epoch
                is commonly smaller than the others, if the size of the dataset
                is not divisible by the batch size.
                The generator is expected to loop over its data
                indefinitely. An epoch finishes when `steps_per_epoch`
                batches have been seen by the model.
            生成器:一个生成器或`Sequence`(`keras.utils.Sequence`) 对象,使用多进程时避免出现重复数据。
                生成器输出必须是以下之一
                - 元组 `(inputs, targets)`
                - 元组`(inputs, targets, sample_weights)`.
                这个元组(单个生成器的输出)是单个批处理。因此,这个元组中的所有数组必须具有相同的长度(等于该批的
                大小)。不同批次可能有不同的尺寸。例如,如果数据集的大小不能被批量大小整除,那么时代的最后一批通常
                比其他批量小。生成器预期无限期地循环其数据。一个周期结束时,`steps_per_epoch`批次已经看到的模型。
            steps_per_epoch: Total number of steps (batches of samples)
                to yield from `generator` before declaring one epoch
                finished and starting the next epoch. It should typically
                be equal to the number of samples of your dataset
                divided by the batch size.
                Optional for `Sequence`: if unspecified, will use
                the `len(generator)` as a number of steps.
            steps_per_epoch:在声明一个周期完成并开始下一个周期之前,从“生成器”中产生的步骤(样本批次数)的总数。它通常
                应该等于数据集的样本数量除以批次大小。
                可选的“序列”:如果未指定,将使用len(generator)`作为步骤数量。
            epochs: Integer, total number of iterations on the data.
                Note that in conjunction with initial_epoch, the parameter
                epochs is to be understood as "final epoch". The model is
                not trained for n steps given by epochs, but until the
                epoch epochs is reached.
            epochs: 整型,数据迭代的总数量。
                请注意,结合conjunction,参数周期被理解为“最终周期”。该模型不是由周期给出的N个步骤训练,而是周期总数。
            verbose: Integer. 0, 1, or 2. Verbosity mode.
                0 = silent, 1 = progress bar, 2 = one line per epoch.
            verbose: 0, 1, 或 2.冗长模式
                0 = 沉默, 1 = 进度条, 2 = 每个周期一行.
            callbacks: List of `keras.callbacks.Callback` instances.
                List of callbacks to apply during training.
                See [callbacks](/callbacks).
            callbacks:`keras.callbacks.Callback` 实例列表。
                在训训过程中应用的回调列表。
                参加[callbacks](/callbacks).
            validation_data: This can be either
                - a generator for the validation data
                - a tuple `(inputs, targets)`
                - a tuple `(inputs, targets, sample_weights)`.
            validation_data:可以是以下之一
                - 验证数据集生成器
                - 元组`(inputs, targets)`
                - 元组`(inputs, targets, sample_weights)`.
            validation_steps: Only relevant if `validation_data`
                is a generator. Total number of steps (batches of samples)
                to yield from `validation_data` generator before stopping
                at the end of every epoch. It should typically
                be equal to the number of samples of your
                validation dataset divided by the batch size.
                Optional for `Sequence`: if unspecified, will use
                the `len(validation_data)` as a number of steps.
            validation_steps:如果validation_data`是生成器有效。在每个周期结束前停止从`validation_data`生成器生成的步
                骤总数(批样本)。通常应该等同于批处理大小除以验证数据集的样本数量。
                `Sequence`可选,如果不确定,使用`len(validation_data)` 作为步骤数。
            class_weight: Optional dictionary mapping class indices (integers)
                to a weight (float) value, used for weighting the loss function
                (during training only). This can be useful to tell the model to
                "pay more attention" to samples from an under-represented class.
            max_queue_size: Integer. Maximum size for the generator queue.
                If unspecified, `max_queue_size` will default to 10.
            max_queue_size:整型。生成队列的最大大小。如果未指定,`max_queue_size` 将默认为10。
            workers: Integer. Maximum number of processes to spin up
                when using process-based threading.
                If unspecified, `workers` will default to 1. If 0, will
                execute the generator on the main thread.
            workers: 整型。使用基于进程的线程时要处理的进程的最大数目。如果没有明确规定, `workers` 将默认为1。
                如果0,将在主线程上执行生成器。
            use_multiprocessing: Boolean.
                If `True`, use process-based threading.
                If unspecified, `use_multiprocessing` will default to `False`.
                Note that because this implementation relies on multiprocessing,
                you should not pass non-picklable arguments to the generator
                as they can't be passed easily to children processes.
            use_multiprocessing:布尔型
                如果是'true',则使用基于进程的线程
                如果未指定,`use_multiprocessing`将默认为`False`。
                注意,因为这个实现依赖于多处理,所以不应该将non-picklable参数传递给生成器,因为它们不容易传递给子进程。
            shuffle: Boolean (whether to shuffle the order of the batches at
                the beginning of each epoch. Only used with instances
                of `Sequence` (`keras.utils.Sequence`).
                Has no effect when `steps_per_epoch` is not `None`.
            shuffle:布尔型(是否在每个周期前洗牌训练数据)或str(for 'batch')
                    “batch”是处理HDF5数据限制的一个特殊选项;它以批量大小的块进行洗牌。
                    当“step_per_epoch”不是“None”时没有效果。
            initial_epoch: Integer.
                Epoch at which to start training
                (useful for resuming a previous training run).
            initial_epoch: 整型。
                开始训练的周期
                (有助于恢复以前的训练)

        # Returns
        返回
            A `History` object.
            历史对象

        # Raises
        补充
            RuntimeError: if the model was never compiled.
            RuntimeError: 如果模型没有编译.
            ValueError: In case the generator yields data in an invalid format.
            ValueError: 如果生成器生成无效格式的数据.

        # Example
        例如

        ```python
            def generate_arrays_from_file(path):
                while True:
                    with open(path) as f:
                        for line in f:
                            # create Numpy arrays of input data
                            # and labels, from each line in the file
                            # 输入数据生成Numpy数组
                            # 标签,文件的每行
                            x, y = process_line(line)
                            yield (x, y)

            model.fit_generator(generate_arrays_from_file('/my_file.txt'),
                                steps_per_epoch=1000, epochs=10)
        ```
        """
        if not self.built:
            raise RuntimeError('The model needs to be compiled '
                               'before being used.')
        return self.model.fit_generator(generator,
                                        steps_per_epoch,
                                        epochs,
                                        verbose=verbose,
                                        callbacks=callbacks,
                                        validation_data=validation_data,
                                        validation_steps=validation_steps,
                                        class_weight=class_weight,
                                        max_queue_size=max_queue_size,
                                        workers=workers,
                                        use_multiprocessing=use_multiprocessing,
                                        shuffle=shuffle,
                                        initial_epoch=initial_epoch)

    @interfaces.legacy_generator_methods_support
    def evaluate_generator(self, generator, steps=None,
                           max_queue_size=10, workers=1,
                           use_multiprocessing=False):
        """Evaluates the model on a data generator.
            基于数据生成器评估模型

        The generator should return the same kind of data
        as accepted by `test_on_batch`.
        生成器返回`test_on_batch`.接受的同类数据。

        # Arguments
        参数
            generator: Generator yielding tuples (inputs, targets)
                or (inputs, targets, sample_weights)
            generator:生成器生成元组 (inputs, targets)或(inputs, targets, sample_weights)
            steps: Total number of steps (batches of samples)
                to yield from `generator` before stopping.
                Optional for `Sequence`: if unspecified, will use
                the `len(generator)` as a number of steps.
            steps:步骤总数(样本批次总数),在停止前从生成器生成。
                `Sequence`可选:如果不确定,使用`len(generator)`作为步骤数量。
            max_queue_size: maximum size for the generator queue
            max_queue_size:生成队列的最大数量
            workers: maximum number of processes to spin up
            workers: 旋转过程的最大数量
            use_multiprocessing: if True, use process based threading.
                Note that because this implementation
                relies on multiprocessing, you should not pass
                non picklable arguments to the generator
                as they can't be passed easily to children processes.
             use_multiprocessing: 如果 True,使用过程基于线程。
                注意,因为这个实现依赖于多处理,所以不应该将非picklable参数传递给生成器,因为它们不容易传递给子进程。

        # Returns
        返回
            Scalar test loss (if the model has no metrics)
            or list of scalars (if the model computes other metrics).
            The attribute `model.metrics_names` will give you
            the display labels for the scalar outputs.
            标量测试损失(如果模型没有度量)或标量列表(如果模型计算其他度量)。属性`model.metrics_names`将为标量输出
            提供显示标签。

        # Raises
        补充
            RuntimeError: if the model was never compiled.
            RuntimeError: 模型未编译
        """
        if not self.built:
            raise RuntimeError('The model needs to be compiled '
                               'before being used.')
        return self.model.evaluate_generator(generator,
                                             steps,
                                             max_queue_size=max_queue_size,
                                             workers=workers,
                                             use_multiprocessing=use_multiprocessing)

    @interfaces.legacy_generator_methods_support
    def predict_generator(self, generator, steps=None,
                          max_queue_size=10, workers=1,
                          use_multiprocessing=False, verbose=0):
        """Generates predictions for the input samples from a data generator.
        为输入样本(来自数据生成器)生成预测

        The generator should return the same kind of data as accepted by
        `predict_on_batch`.
        生成器应该返回与`predict_on_batch`.相同类型的数据。

        # Arguments
        参数
            generator: generator yielding batches of input samples.
            generator: 生成器生成输入样本的批次.
            steps: Total number of steps (batches of samples)
                to yield from `generator` before stopping.
                Optional for `Sequence`: if unspecified, will use
                the `len(generator)` as a number of steps.
            steps:步骤总数(样本批次总数),在停止前从生成器生成。
                Sequence`可选:如果不确定,使用`len(generator)`作为步骤数量。
            max_queue_size: maximum size for the generator queue
            max_queue_size: 生成队列的最大数量
            workers: maximum number of processes to spin up
            workers:旋转过程的最大数量
            use_multiprocessing: if True, use process based threading.
                Note that because this implementation
                relies on multiprocessing, you should not pass
                non picklable arguments to the generator
                as they can't be passed easily to children processes.
            use_multiprocessing:如果 True,使用过程基于线程。
                注意,因为这个实现依赖于多处理,所以不应该将非picklable参数传递给生成器,因为它们不容易传递给子进程。
            verbose: verbosity mode, 0 or 1.
            verbose: 冗余模式, 0 或 1.

        # Returns
        返回
            A Numpy array of predictions.
            预测的Numpy数组
        """
        if not self.built:
            self.build()
        return self.model.predict_generator(generator, steps,
                                            max_queue_size=max_queue_size,
                                            workers=workers,
                                            use_multiprocessing=use_multiprocessing,
                                            verbose=verbose)

    def get_config(self):
        if isinstance(self.layers[0], legacy_layers.Merge):
            return self.legacy_get_config()

        config = []
        for layer in self.layers:
            config.append({'class_name': layer.__class__.__name__,
                           'config': layer.get_config()})
        return copy.deepcopy(config)

    @classmethod
    def from_config(cls, config, custom_objects=None):
        if 'class_name' not in config[0] or config[0]['class_name'] == 'Merge':
            return cls.legacy_from_config(config)

        model = cls()
        for conf in config:
            layer = layer_module.deserialize(conf, custom_objects=custom_objects)
            model.add(layer)
        return model

    def legacy_get_config(self):
        """Retrieves the model configuration as a Python list.
        用Python列表恢复模型配置

        # Returns
        返回
            A list of dicts (each dict is a layer config).
            字典列表(每个字典是一个层配置)
        """
        config = []
        if isinstance(self.layers[0], legacy_layers.Merge):
            assert hasattr(self.layers[0], 'layers')
            layers = []
            for layer in self.layers[0].layers:
                layer_config = {'class_name': layer.__class__.__name__,
                                'config': layer.get_config()}
                layers.append(layer_config)
            merge_config = self.layers[0].get_config()
            merge_config['layers'] = layers
            config.append({'class_name': 'Merge', 'config': merge_config})
        else:
            config.append({'class_name': self.layers[0].__class__.__name__,
                           'config': self.layers[0].get_config()})
        for layer in self.layers[1:]:
            config.append({'class_name': layer.__class__.__name__,
                           'config': layer.get_config()})
        return copy.deepcopy(config)

    @classmethod
    def legacy_from_config(cls, config, layer_cache=None):
        """Load a model from a legacy configuration.
        从遗留配置加载模型。

        # Arguments
        参数
            config: dictionary with configuration.
            config: 配置字典.
            layer_cache: cache to draw pre-existing layer.
            layer_cache: 缓存绘制预先存在的层.

        # Returns
        返回
            The loaded Model.
            加载的模型
        """
        if not layer_cache:
            layer_cache = {}

        def normalize_legacy_config(conf):
            if 'class_name' not in conf:
                class_name = conf['name']
                name = conf.get('custom_name')
                conf['name'] = name
                return {'class_name': class_name,
                        'config': conf}
            return conf

        # the model we will return
        # 返回的模型
        model = cls()

        def get_or_create_layer(layer_data):
            name = layer_data['config'].get('name')
            if name in layer_cache:
                return layer_cache[name]
            layer = layer_module.deserialize(layer_data)
            layer_cache[name] = layer
            return layer

        first_layer = config[0]
        first_layer = normalize_legacy_config(first_layer)
        if first_layer['class_name'] == 'Merge':
            merge_inputs = []
            first_layer_config = first_layer['config']
            for merge_input_config in first_layer_config.pop('layers'):
                merge_input = layer_module.deserialize(merge_input_config)
                merge_inputs.append(merge_input)
            first_layer_config['layers'] = merge_inputs
            merge = legacy_layers.Merge.from_config(first_layer_config)
            model.add(merge)
        else:
            layer = get_or_create_layer(first_layer)
            model.add(layer)

        for conf in config[1:]:
            conf = normalize_legacy_config(conf)
            layer = get_or_create_layer(conf)
            model.add(layer)
        return model


def _clone_functional_model(model, input_tensors=None):
    """Clone a functional `Model` instance.
    克隆一个函数“模型”实例。

    Model cloning is similar to calling a model on new inputs,
    except that it creates new layers (and thus new weights) instead
    of sharing the weights of the existing layers.

    # Arguments
    参数
        model: Instance of `Model`.
        model:  `Model`实例。
        input_tensors: optional list of input tensors
            to build the model upon. If not provided,
            placeholders will be created.
        input_tensors:可选的输入张量列表以建立模型。如果没有提供,占位符将被创建。

    # Returns
    返回
        An instance of `Model` reproducing the behavior
        of the original model, on top of new inputs tensors,
        using newly instantiated weights.
        使用新实例化的权重,在新输入张量之上再现原始模型的行为的“模型”实例。

    # Raises
    补充
        ValueError: in case of invalid `model` argument value.
        ValueError: 如果是无效的 `model` 参数值.
    """
    if not isinstance(model, Model):
        raise ValueError('Expected `model` argument '
                         'to be a `Model` instance, got ', model)
    if isinstance(model, Sequential):
        raise ValueError('Expected `model` argument '
                         'to be a functional `Model` instance, '
                         'got a `Sequential` instance instead:', model)

    layer_map = {}  # Cache for created layers.  缓存建立的层
    tensor_map = {}  # Map {reference_tensor: (corresponding_tensor, mask)} 映射{对应张量: (corresponding_tensor, mask)}
    if input_tensors is None:
        # Create placeholders to build the model on top of.
        # 创建占位符来在上面构建模型。
        input_layers = []
        input_tensors = []
        for layer in model.input_layers:
            input_tensor = Input(batch_shape=layer.batch_input_shape,
                                 dtype=layer.dtype,
                                 sparse=layer.sparse,
                                 name=layer.name)
            input_tensors.append(input_tensor)
            # Cache newly created input layer.
            # 缓存新创建的输入层。
            newly_created_input_layer = input_tensor._keras_history[0]
            layer_map[layer] = newly_created_input_layer
        for original_input_layer, cloned_input_layer in zip(model.input_layers, input_layers):
            layer_map[original_input_layer] = cloned_input_layer
    else:
        # Make sure that all input tensors come from a Keras layer.
        # 确保所有输入张量来自Keras层。
        # If tensor comes from an input layer: cache the input layer.
        # 如果张量来自输入层:缓存输入层。
        input_tensors = topology._to_list(input_tensors)
        _input_tensors = []
        for i, x in enumerate(input_tensors):
            if not K.is_keras_tensor(x):
                name = model.input_layers[i].name
                input_tensor = Input(tensor=x,
                                     name='input_wrapper_for_' + name)
                _input_tensors.append(input_tensor)
                # Cache newly created input layer.
                original_input_layer = x._keras_history[0]
                newly_created_input_layer = input_tensor._keras_history[0]
                layer_map[original_input_layer] = newly_created_input_layer
            else:
                _input_tensors.append(x)
        input_tensors = _input_tensors

    for x, y in zip(model.inputs, input_tensors):
        tensor_map[x] = (y, None)  # tensor, mask

    # Iterated over every node in the reference model, in depth order.
    # 在参考模型中的每个节点上迭代,按深度顺序。
    depth_keys = list(model._nodes_by_depth.keys())
    depth_keys.sort(reverse=True)
    for depth in depth_keys:
        nodes = model._nodes_by_depth[depth]
        for node in nodes:
            # Recover the corresponding layer.
            # 恢复相应的层。
            layer = node.outbound_layer

            # Get or create layer.
            # 获取或创建层。
            if layer not in layer_map:
                # Clone layer.
                # 克隆层
                new_layer = layer.__class__.from_config(layer.get_config())
                layer_map[layer] = new_layer
                layer = new_layer
            else:
                # Reuse previously cloned layer.
                # 重用以前克隆的层。
                layer = layer_map[layer]
                # Don't call InputLayer multiple times.
                # 不要多次调用输入层。
                if isinstance(layer, topology.InputLayer):
                    continue

            # Gather inputs to call the new layer.
            # 收集输入以调用新层。
            reference_input_tensors = node.input_tensors
            reference_output_tensors = node.output_tensors

            # If all previous input tensors are available in tensor_map,
            # then call node.inbound_layer on them.
            # 如果所有以前的输入张量都在tensor_map中可用,那么调用inbound_layer层。
            computed_data = []  # List of tuples (input, mask). 元组 (input, mask)列表
            for x in reference_input_tensors:
                if x in tensor_map:
                    computed_data.append(tensor_map[x])

            if len(computed_data) == len(reference_input_tensors):
                # Call layer.
                # 调用层
                if node.arguments:
                    kwargs = node.arguments
                else:
                    kwargs = {}
                if len(computed_data) == 1:
                    computed_tensor, computed_mask = computed_data[0]
                    if has_arg(layer.call, 'mask'):
                        if 'mask' not in kwargs:
                            kwargs['mask'] = computed_mask
                    output_tensors = topology._to_list(
                        layer(computed_tensor, **kwargs))
                    output_masks = topology._to_list(
                        layer.compute_mask(computed_tensor,
                                           computed_mask))
                    computed_tensors = [computed_tensor]
                    computed_masks = [computed_mask]
                else:
                    computed_tensors = [x[0] for x in computed_data]
                    computed_masks = [x[1] for x in computed_data]
                    if has_arg(layer.call, 'mask'):
                        if 'mask' not in kwargs:
                            kwargs['mask'] = computed_masks
                    output_tensors = topology._to_list(
                        layer(computed_tensors, **kwargs))
                    output_masks = topology._to_list(
                        layer.compute_mask(computed_tensors,
                                           computed_masks))
                # Update tensor_map.
                # 更新 tensor_map.
                for x, y, mask in zip(reference_output_tensors,
                                      output_tensors,
                                      output_masks):
                    tensor_map[x] = (y, mask)

    # Check that we did compute the model outputs,
    # then instantiate a new model from inputs and outputs.
    # 检查我们是否计算模型输出,然后从输入和输出实例化一个新模型。
    output_tensors = []
    for x in model.outputs:
        assert x in tensor_map, 'Could not compute output ' + str(x)
        tensor, _ = tensor_map[x]
        output_tensors.append(tensor)
    return Model(input_tensors, output_tensors, name=model.name)


def _clone_sequential_model(model, input_tensors=None):
    """Clone a `Sequential` model instance.
    克隆序列模型实例

    Model cloning is similar to calling a model on new inputs,
    except that it creates new layers (and thus new weights) instead
    of sharing the weights of the existing layers.
    模型克隆类似于在新输入上调用模型,只是它创建新层(从而创建新权重),而不是共享现有层的权重。

    # Arguments
    参数
        model: Instance of `Sequential`.
        model: 序列实例.
        input_tensors: optional list of input tensors
            to build the model upon. If not provided,
            placeholders will be created.
        input_tensors:可选的输入张量列表以建立模型。如果没有提供,占位符将被创建。

    # Returns
    返回
        An instance of `Sequential` reproducing the behavior
        of the original model, on top of new inputs tensors,
        using newly instantiated weights.
        使用新实例化的权重,在新输入张量之上再现原始模型的行为的“序列”实例。


    # Raises
    补充
        ValueError: in case of invalid `model` argument value.
        ValueError: 在“模型”参数值无效的情况下。
    """
    if not isinstance(model, Sequential):
        raise ValueError('Expected `model` argument '
                         'to be a `Sequential` model instance, '
                         'but got:', model)

    def clone(layer):
        return layer.__class__.from_config(layer.get_config())

    layers = [clone(layer) for layer in model.layers]
    if input_tensors is None:
        return Sequential(layers=layers, name=model.name)
    else:
        if len(topology._to_list(input_tensors)) != 1:
            raise ValueError('To clone a `Sequential` model, we expect '
                             ' at most one tensor '
                             'as part of `input_tensors`.')
        x = topology._to_list(input_tensors)[0]
        if K.is_keras_tensor(x):
            origin_layer = x._keras_history[0]
            if isinstance(origin_layer, topology.InputLayer):
                return Sequential(layers=[origin_layer] + layers,
                                  name=model.name)
            else:
                raise ValueError('Cannot clone a `Sequential` model on top '
                                 'of a tensor that comes from a Keras layer '
                                 'other than an `InputLayer`. '
                                 'Use the functional API instead.')
        input_tensor = Input(tensor=x,
                             name='input_wrapper_for_' + str(x.name))
        input_layer = input_tensor._keras_history[0]
        return Sequential(layers=[input_layer] + layers, name=model.name)


def clone_model(model, input_tensors=None):
    """Clone any `Model` instance.
    克隆任何“模型”实例。

    Model cloning is similar to calling a model on new inputs,
    except that it creates new layers (and thus new weights) instead
    of sharing the weights of the existing layers.
    模型克隆类似于在新输入上调用模型,只是它创建新层(从而创建新权重),而不是共享现有层的权重。

    # Arguments
    参数
        model: Instance of `Model`
            (could be a functional model or a Sequential model).
        model: `Model`实例
               (可以是一个函数模型或者序列模型 ).
        input_tensors: optional list of input tensors
            to build the model upon. If not provided,
            placeholders will be created.
        input_tensors:可选的输入张量列表以建立模型。如果没有提供,占位符将被创建。

    # Returns
    返回
        An instance of `Model` reproducing the behavior
        of the original model, on top of new inputs tensors,
        using newly instantiated weights.
        “模型”的实例,使用新实例化的权重,在新输入张量之上再现原始模型的行为。

    # Raises
    补充
        ValueError: in case of invalid `model` argument value.
        ValueError: 在“模型”参数值无效的情况下。
    """
    if isinstance(model, Sequential):
        return _clone_sequential_model(model, input_tensors=input_tensors)
    else:
        return _clone_functional_model(model, input_tensors=input_tensors)

代码执行

Keras详细介绍

英文:https://keras.io/

中文:http://keras-cn.readthedocs.io/en/latest/

实例下载

https://github.com/keras-team/keras

https://github.com/keras-team/keras/tree/master/examples

完整项目下载

方便没积分童鞋,请加企鹅452205574,共享文件夹。

包括:代码、数据集合(图片)、已生成model、安装库文件等。

猜你喜欢

转载自blog.csdn.net/wyx100/article/details/81561229