目录

  • 1、Tensorflow与Keras
  • 2、安装内置Keras的Tensorflow
  • 3、Tensorflow内置的Keras教程
    • 3.1、导入tf.keras
    • 3.2、创建一个简单的模型
      • 3.2.1、顺序模型(Sequential model)
      • 3.2.2、设置keras层(layer)
    • 3.3、训练和评估
      • 3.3.1、配置训练
      • 3.3.2、使用NumPy数据作为训练数据
      • 3.3.3、使用 tf.data作为训练数据
      • 3.3.4、评估和预测
    • 3.4、构建高级模型
      • 3.4.1、函数式API
      • 3.4.2、模型子类化(Model subclassing)
      • 3.4.3、自定义层
    • 3.5、回调
    • 3.6、保存与载入
      • 3.6.1、仅保存权重
      • 3.6.2、仅保存配置
      • 3.6.3、保存整个模型
    • 3.7、Eager execution
    • 3.8、分布式训练
      • 3.8.1 、Estimators
      • 3.8.2、多GPU训练

1、Tensorflow与Keras

Tensorflow和Keras原本是两个深度学习的框架。Keras有着更高级的API,构建模型要比Tensorflow简单许多;Keras有许多后端(backend)可以选,Tensorflow就是其中一种后端。
2017年01月17日,Keras的作者、谷歌AI研究员Francois Chollet宣布了一条激动人心的消息:Keras将会成为第一个被添加到TensorFlow核心中的高级别框架,这将会让Keras变成Tensorflow的默认API。也就是说Tensorflow内置Keras了。

2、安装内置Keras的Tensorflow

最新版Tensorflow已经内置了Keras模块(最新的Tensorflow版本是1.11.0),其对应的Keras的版本为2.1.6-tf,只要安装了Tensorflow 1.11.0及以上版本,就已经能使用Tensorflow内置的Keras了。

3、Tensorflow内置的Keras教程

此部分文档是官分文档的翻译,根据自己的理解进行了翻译,帮助大家入门。原文链接Tensorflow Keras

3.1、导入tf.keras

tf.kerasKeras API的Tensorflow实现,是一个用于构建和训练模型的高级API,其中包括对TensorFlow特定功能的一流支持,例如Eager Executiontf.data PipelineEstimators。 tf.keras使TensorFlow更易于使用,而不会牺牲灵活性和性能。
导入tf.keras的代码为:

import tensorflow as tf
from tensorflow.keras import layers
# 打印Tensorflow和内置的Keras版本
print(tf.VERSION)
print(tf.keras.__version__)

tf.keras可以运行任何与Keras兼容的代码,但请记住:

  • 最新TensorFlow版本中的tf.keras版本可能与PyPI的最新keras版本不同。 可打印tf.keras.version来检查版本。
  • 保存模型的权重时,tf.keras默认为检查点格式( checkpoint format)。 要使用HDF5保存权重时,传入参数save_format =‘h5’

3.2、创建一个简单的模型

3.2.1、顺序模型(Sequential model)

在Keras中,您可以组装层(Layer)来构建模型(Model)。 最常见的模型类型是层的堆叠:tf.keras.Sequential模型。
构建一个简单的,完全连接的网络(即多层感知器):

model = tf.keras.Sequential()
# 添加一个有64个单元全连接层到模型
# 顺便说一句,densely-connected layer=fully-connected layer
model.add(layers.Dense(64, activation='relu'))
# 再添加一个
model.add(layers.Dense(64, activation='relu'))
# 添加一个有10个输出单元的softmax层
model.add(layers.Dense(10, activation='softmax'))

3.2.2、设置keras层(layer)

tf.keras.layers一些常见的构造函数参数:

  • activation:设置层的激活函数。 此参数由内置函数的名称或可调用对象指定。 默认情况下,不使用任何激活。
  • kernel_initializerbias_initializer:分别指定层的核(原文是Kernel,可以理解为weights)和偏置(bias)的初始化器(initializer)。 参数是名称或可调用对象。 默认为“Glorot uniform”初始化器。
  • kernel_regularizerbias_regularizer:分别指定层的核(Kernel)和偏置(bias)的正则化方案,例如L1或L2正则化。 默认情况下,不使用正则化。

下面使用构造函数参数实例化tf.keras.layers.Dense的一些例子:

# 创建一个sigmoid层:
layers.Dense(64, activation='sigmoid')
# 另外一种方法
layers.Dense(64, activation=tf.sigmoid)# 定义一个线性层(linear layer),核矩阵(kernel matrix)使用一个因子为0.01的L1正则化器
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))# 定义一个线性层,偏置向量( bias vector)使用一个因子为0.01的L2正则化器
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))# 定义了一个线性层,核使用的初始化器为orthogonal(核会被初始化为一个随机正交矩阵)
layers.Dense(64, kernel_initializer='orthogonal')# 定义了一个线性层,偏置向量的初始化去器为constant(偏置向量的所有元素都会被初始化为2.0)
layers.Dense(64, bias_initializer=tf.keras.initializers.constant(2.0))

3.3、训练和评估

3.3.1、配置训练

构建模型后,通过调用compile函数编译(原文为configure,我这里翻译为编译):

model = tf.keras.Sequential([
# 添加一个有64个单元的全连接层,激活函数为relu
layers.Dense(64, activation='relu'),
# 再添加一个
layers.Dense(64, activation='relu'),
# 添加一个有10个输出单元的softmax层
layers.Dense(10, activation='softmax')])model.compile(optimizer=tf.train.AdamOptimizer(0.001),loss='categorical_crossentropy',metrics=['accuracy'])

tf.keras.Model.compile有三个重要参数(建议参考下Keras compile document):

  • optimizer:指定优化器。 从tf.train模块传递优化器实例,例如tf.train.AdamOptimizertf.train.RMSPropOptimizertf.train.GradientDescentOptimizer
  • loss:在优化期间最小化的函数。 常见的选择包括均方误差(mse),categorical_crossentropybinary_crossentropy。 损失函数由名称或通过从tf.keras.losses模块传递可调用对象来指定。
  • metrics:设置训练中要输出的指标(原文为metrics,我这里翻译为指标)组成的列表,上面的代码中就只有准确率(accuracy)。 指标是来自tf.keras.metrics模块的字符串名称或可调用对象组成的list。

以下显示了编译模型的几个示例:

# 编译均方误差回归模型(a model for mean-squared error regression)
model.compile(optimizer=tf.train.AdamOptimizer(0.01),loss='mse',       # 最小均方误差(mean squared error)metrics=['mae'])  # 平均绝对误差(mean absolute error)# 编译一个分类模型(a model for categorical classification)
# 多元分类问题例子,比如手写数字识别
model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),loss=tf.keras.losses.categorical_crossentropy,metrics=[tf.keras.metrics.categorical_accuracy])
# 和上面的相同
model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])# 二元分类例子
model.compile(optimizer='rmsprop',loss='binary_crossentropy',metrics=['accuracy'])

可以注意到,上面的程序中第一个的lossmetrics传的是字符串,第二种传的是可调用的对象。

3.3.2、使用NumPy数据作为训练数据

对于小型数据集,请使用内存中的NumPy数组来训练和评估模型。 使用fit方法将模型“拟合”到训练数据:

import numpy as npdata = np.random.random((1000, 32))
labels = np.random.random((1000, 10))model.fit(data, labels, epochs=10, batch_size=32)

tf.keras.Model.fit有三个重要参数:

  • epoch:训练多少个epoch。 一个epoch是对整个训练数据集的一次训练(这是以较小的批次完成的)。
  • batch_size:当传递NumPy数据时,模型将数据分成较小的批次(batch),并在训练期间训练这些批次。 此整数指定每个批次的大小。 请注意,如果样本总数不能被批次大小整除,则最后一批可能会更小。
  • validation_data:在对模型进行原型设计时,若要监控其在某些验证数据集上的性能。 传递由(输入,标签)组成的元组, 模型在每个epoch的末尾显示损失和指标。

这是使用validation_data的示例:

import numpy as npdata = np.random.random((1000, 32))
labels = np.random.random((1000, 10))val_data = np.random.random((100, 32))
val_labels = np.random.random((100, 10))model.fit(data, labels, epochs=10, batch_size=32,validation_data=(val_data, val_labels))

3.3.3、使用 tf.data作为训练数据

若要使用大型数据集或多设备训练,要使用Dateset API ,将tf.data.Dataset实例传递给fit方法:

# 实例化玩具数据集实例:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)
dataset = dataset.repeat()# 在数据集上调用`fit`时,不要忘记指定`steps_per_epoch`
model.fit(dataset, epochs=10, steps_per_epoch=30)

这里,fit方法函数steps_per_epoch参数 - 这是模型在训练集上训练一遍(也就是一个epoch)的训练步数。 由于Dataset生成批次数据,因此此代码段不需要batch_size

如果要传入验证集:

dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32).repeat()val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(32).repeat()model.fit(dataset, epochs=10, steps_per_epoch=30,validation_data=val_dataset,validation_steps=3)

3.3.4、评估和预测

tf.keras.Model.evaluatetf.keras.Model.predict函数可以使用NumPy数组和tf.data.Dataset作为参数。

如果要评估(evaluate)所提供数据的模型的损失和指标(也就是深度学习中评估测试集的损失loss和前面compile函数中metrics指定的指标),代码如下:

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))model.evaluate(data, labels, batch_size=32)model.evaluate(dataset, steps=30)

NumPy数组作为参数,输出模型最后一层的输出(也就是预测一个样本):

result = model.predict(data, batch_size=32)
print(result.shape)

3.4、构建高级模型

3.4.1、函数式API

tf.keras.Sequential模型是一个简单的层的堆叠,不能构建任意模型。 使用Keras函数式API构建复杂的模型,例如:

  • 多输入模型,
  • 多输出模型,
  • 具有共享层的模型(同一层被调用多次),
  • 具有非顺序数据流的模型(例如,residual connections)。

使用函数式API构建模型的方法如下:

  • 调用层实例,并且返回张量(tensor)。
  • 输入张量和输出张量用于定义tf.keras.Model实例。
  • 这个模型的训练就像Sequential模型一样。

以下示例使用函数式API构建一个简单,全连接(fully-connected)的网络:

inputs = tf.keras.Input(shape=(32,))  # Returns a placeholder tensor# 层的实例可调用,参数为tensor,返回一个tensor
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)

实例化指定输入和输出的模型

model = tf.keras.Model(inputs=inputs, outputs=predictions)# 编译这个步骤指定了训练的配置(the training configuration)
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),loss='categorical_crossentropy',metrics=['accuracy'])# 训练5个epoch
model.fit(data, labels, batch_size=32, epochs=5)

3.4.2、模型子类化(Model subclassing)

通过继承tf.keras.Model并定义自己的前向传播来构建完全可自定义的模型。 在__init__函数中创建图层并将它们设置为类实例的属性。 在call函数中定义前向传播。

当启用Eager Execution时,模型子类化特别有用(原文说很有用,但我还不知道)。

  • 关键点: 虽然模型子类化提供了灵活性,但其代价是更高的复杂性和更多的编码错误可能性。 如果可能,请选择函数式API(也就是3.4.1节中提到的方法)。

以下示例显示了继承tf.keras.Model,使用自定义前向传播:

class MyModel(tf.keras.Model):def __init__(self, num_classes=10):super(MyModel, self).__init__(name='my_model')self.num_classes = num_classes# 在__init__函数中定义层self.dense_1 = layers.Dense(32, activation='relu')self.dense_2 = layers.Dense(num_classes, activation='sigmoid')def call(self, inputs):# 在call函数中定义前向传播# 使用在__init__中定义的层x = self.dense_1(inputs)return self.dense_2(x)def compute_output_shape(self, input_shape):# 如果你需要把这个子类化的模型当作一个函数式模型的一部分,那么你需要重载这个函数。# 否则,这个函数是可选的shape = tf.TensorShape(input_shape).as_list()shape[-1] = self.num_classesreturn tf.TensorShape(shape)

实例化上述模型的代码为:

model = MyModel(num_classes=10)model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),loss='categorical_crossentropy',metrics=['accuracy'])model.fit(data, labels, batch_size=32, epochs=5)

3.4.3、自定义层

要自定义层,需要继承 tf.keras.layers.Layer并且实现如下几个函数:

  • build:创建图层的权重。 使用add_weight函数来创建。
  • call:定义前向传播。
  • compute_output_shape:计算在给定的输入的shape时,计算出输出的shape。
  • 可以通过实现get_config方法和from_config方法来序列化层。不过是可选的。

下面是自定义层的示例,它实现了一个使输入(Input)和核(Kernel)矩阵相乘(matmul):

class MyLayer(layers.Layer):def __init__(self, output_dim, **kwargs):self.output_dim = output_dimsuper(MyLayer, self).__init__(**kwargs)def build(self, input_shape):shape = tf.TensorShape((input_shape[1], self.output_dim))# 为此层创建一个可训练的权重self.kernel = self.add_weight(name='kernel',shape=shape,initializer='uniform',trainable=True)# 确保在函数结束时调用下面的语句super(MyLayer, self).build(input_shape)def call(self, inputs):# 这里定义了这层要实现的操作,也就是前向传播的操作return tf.matmul(inputs, self.kernel)def compute_output_shape(self, input_shape):# 计算输出tensor的shapeshape = tf.TensorShape(input_shape).as_list()shape[-1] = self.output_dimreturn tf.TensorShape(shape)def get_config(self):base_config = super(MyLayer, self).get_config()base_config['output_dim'] = self.output_dimreturn base_config@classmethoddef from_config(cls, config):return cls(**config)

使用自定义层:

model = tf.keras.Sequential([MyLayer(10),layers.Activation('softmax')])model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),loss='categorical_crossentropy',metrics=['accuracy'])model.fit(data, labels, batch_size=32, epochs=5)

3.5、回调

回调是传递给模型的对象,用于在训练模型期间自定义和扩展其行为。 可以编写自己的自定义回调,或使用以下内置的tf.keras.callbacks

  • tf.keras.callbacks.ModelCheckpoint:定期保存模型的检查点(checkpoint)。
  • tf.keras.callbacks.LearningRateScheduler:动态改变学习率。
  • tf.keras.callbacks.EarlyStopping:在校验集的性能停止提升时,中断训练。
  • tf.keras.callbacks.TensorBoard:使用TensorBoard监控模型的行为。

若要使用tf.keras.callbacks.Callback,请将其传递给模型的fit方法:

callbacks = [# 如果`val_loss`在超过两个epoch都没有提升,那么中断训练tf.keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),# 把TensorBoard的日志写入文件夹`./logs`tf.keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,validation_data=(val_data, val_labels))

3.6、保存与载入

3.6.1、仅保存权重

参考tf.keras.Model.save_weights

# 把权重保存为TensorFlow Checkpoint文件
model.save_weights('./weights/my_model')# 载入权重。要求模型和保存权重的模型具有相同的架构
model.load_weights('./weights/my_model')

如果要把权重保存为Keras的HDF5格式,可使用如下代码

# 把权重保存为HDF5这种格式的文件
model.save_weights('my_model.h5', save_format='h5')# 载入权重
model.load_weights('my_model.h5')

3.6.2、仅保存配置

模型的配置(原文为model’s configuration,可以理解为模型的结构)是可以保存的。 这可以在没有任何权重的情况下序列化模型体系结构。 即使没有定义原始模型的代码,保存的配置也可以重新创建和初始化相同的模型。 Keras支持JSON和YAML序列化格式:
把模型序列化为json字符串

json_string = model.to_json()
print(json_string)

从json字符串恢复模型:

fresh_model = tf.keras.models.model_from_json(json_string)

把模型序列化为YAML格式:

yaml_string = model.to_yaml()
print(yaml_string)

从YAML字符串恢复模型:

fresh_model = tf.keras.models.model_from_yaml(yaml_string)
  • 注意:子类化模型不可序列化,因为它们的体系结构由call函数中的Python代码定义。

3.6.3、保存整个模型

如果保存模型结构与权重(甚至还包括优化器的配置),下次可以恢复模型结构和权重接着训练且不需要访问原始代码。

# 创建一个简单的模型
model = tf.keras.Sequential([layers.Dense(10, activation='softmax', input_shape=(32,)),layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',loss='categorical_crossentropy',metrics=['accuracy'])
model.fit(data, labels, batch_size=32, epochs=5)# 将整个模型保存到HDF5文件
model.save('my_model.h5')# 重新创建完全相同的模型,包括权重和优化器。
model = tf.keras.models.load_model('my_model.h5')

3.7、Eager execution

Eager execution是一个必要的编程环境,可以立即评估操作。 这对于Keras不是必需的,但是由tf.keras支持,对于检查程序和调试很有用。
所有tf.keras API都与Eager execution兼容。 虽然可以使用顺序模型和函数式API,但是Eager execution尤其有利于模型子类化和构建自定义层 。
有关使用具有自定义训练和tf.GradientTape的Keras模型的示例,请参阅Eager execution 指南。

3.8、分布式训练

3.8.1 、Estimators

Estimators API用于分布式环境的训练模型的API。 可以导出模型进行大型数据集的分布式训练,并得到可以商用的模型。
使用tf.keras.estimator.model_to_estimator将模型转换为tf.estimator.Estimator对象,就可以使用tf.estimator API训练tf.keras.Model。 请参阅Creating Estimators from Keras models。

model = tf.keras.Sequential([layers.Dense(10,activation='softmax'),layers.Dense(10,activation='softmax')])model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),loss='categorical_crossentropy',metrics=['accuracy'])
# 把模型转换为Estimator
estimator = tf.keras.estimator.model_to_estimator(model)
  • 注意:要检查Estimator的输入函数和查看数据时,请启用Eager Execution。

3.8.2、多GPU训练

tf.keras模型可以使用tf.contrib.distribute.DistributionStrategy在多个GPU上训练。 此API在多个GPU上提供分布式训练,几乎不对现有代码进行任何更改。
目前,tf.contrib.distribute.MirroredStrategy是唯一受支持的分布式策略。 要将DistributionStrategy与Keras一起使用,请使用tf.keras.estimator.model_to_estimatortf.keras.Model转换为tf.estimator.Estimator,然后训练Estimator。
以下示例在单个计算机上的多个GPU之间分布式训练tf.keras.Model
首先,定义一个简单的模型:

model = tf.keras.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10,)))
model.add(layers.Dense(1, activation='sigmoid'))optimizer = tf.train.GradientDescentOptimizer(0.2)model.compile(loss='binary_crossentropy', optimizer=optimizer)
model.summary()

定义输入管线(Input Pipeline)。input_fn返回一个tf.data.Dataset对象,用于在多个设备之间分配数据 - 每个设备处理输入数据(Input batch)的一部分。

def input_fn():x = np.random.random((1024, 10))y = np.random.randint(2, size=(1024, 1))x = tf.cast(x, tf.float32)dataset = tf.data.Dataset.from_tensor_slices((x, y))dataset = dataset.repeat(10)dataset = dataset.batch(32)return dataset

接下来,创建一个tf.estimator.RunConfig并将train_distribute参数设置为tf.contrib.distribute.MirroredStrategy。 创建MirroredStrategy时,可以指定设备列表或设置num_gpus参数。 默认使用所有可用的GPU,如下所示:

strategy = tf.contrib.distribute.MirroredStrategy()
config = tf.estimator.RunConfig(train_distribute=strategy)

将Keras模型转换为tf.estimator.Estimator实例:

keras_estimator = tf.keras.estimator.model_to_estimator(keras_model=model,config=config,model_dir='/tmp/model_dir')

最后,通过提供input_fn和steps参数来训练Estimator实例:

keras_estimator.train(input_fn=input_fn, steps=10)

Tensorflow keras入门教程相关推荐

  1. keras入门教程 1.线性回归建模(快速入门)

    Keras入门教程 1.线性回归建模(快速入门) 2.线性模型的优化 3.波士顿房价回归 (MPL) 4.卷积神经网络(CNN) 5.使用LSTM RNN 进行时间序列预测 6.Keras 预训练模型 ...

  2. Keras入门教程 3.波士顿房价回归 (MPL)

    Keras入门教程 1.线性回归建模(快速入门) 2.线性模型的优化 3.波士顿房价回归 (MPL) 4.卷积神经网络(CNN) 5.使用LSTM RNN 进行时间序列预测 6.Keras 预训练模型 ...

  3. keras 入门教程

    keras 入门教程 Keras 是一个用于构建和训练深度学习模型的高级 Python 库.它的目的是简化深度学习模型的构建过程,使得深度学习更易于理解和使用.如果你想入门 Keras,可以参考以下教 ...

  4. tensorflow.keras入门1

    tensorflow.keras入门1-基本函数介绍 目前keras API 已经整合到 tensorflow中,在tensorflow中通过tf.keras就可以调用keras. import te ...

  5. Tensorflow Slim入门教程(1)

    slim入门教程 slim入门教程 1. Variable 2. Layers 2.1 slim.bias_add 2.2 slim.batch_norm 2.3 slim.conv2d 2.4 sl ...

  6. TensorFlow(keras)入门课程--06 CNN用于猫狗数据集

    目录 1 简介 在本节中,我们将学习如何使用卷积神经网络,并使用更大的数据集,这有助于避免过度拟合的问题! 2 使用更大的数据集进行训练-猫和狗 在之前的实验中,训练了一个马与人类数据的分类器.尽管在 ...

  7. TensorFlow Keras 官方教程

    TensorFlow版本:1.10.0 > Guide >教程地址:https://tensorflow.google.cn/guide/keras Keras 简介 Keras是一个用于 ...

  8. 深度学习框架之Keras入门教程

    introduction and install example introduction and install Keras是一种high level的神经网路的Python API,它可以在Ten ...

  9. TensorFlow v1 入门教程

    目录 Tensor 计算图 Eager mode Tensorboard Save and Restore 模型保存 模型恢复 tensorflow v2 版本现在已经全面用keras 封装了,运行时 ...

最新文章

  1. php程序中用户名含特殊字符怎么办,php中包含ñ等特殊字符
  2. return 与 exit()的区别--return退出本函数,exit()退出整个程序
  3. Visual Studio 2019 首个预览版本抢先看,有啥新功能?
  4. 浅谈ajax中get与post的区别,以及ajax中的乱码问题的解决方法
  5. Bootstrap3 滚动监听插件的方法
  6. Django学习--form(表单)
  7. QT 5.3 VS2010 中文
  8. swapLexOrder
  9. spark RDD概念及组成详解
  10. Springboot--Ehcache-Jpa (1)
  11. Intel altera opencl 入门
  12. 【PPT技巧】为PPT寻找好看的英文字体(English nice-looking font free)并安装到Windows
  13. 带管理职位面试中遇到的常见经典问题的回答
  14. CISSP-OSG-每章小结梳理
  15. springboot自定义启动logo
  16. 仓储系统主要注意事项
  17. linux批量对文件改名,在Linux中对文件进行批量重命名文件的方法
  18. OFDMA,LFDMA以及IFDMA的PAPR对比仿真
  19. MyBatis -- resultType 和 resultMap
  20. MATLAB-网页访问与关闭

热门文章

  1. 小程序列表页tab切换swiper并滚动到上次位置
  2. numpy的学习,全是源码,勿喷
  3. swiper 重新初始化
  4. jsp返回上一个页面并刷新
  5. 弘辽科技:新手开淘宝店的步骤有哪些?如何起步?
  6. 一键彻底关闭WIN10自动更新_BlockWin10AU
  7. @开发者:个推小程序消息推送解决方案来了
  8. 统计遗传学:第三章,群体遗传
  9. 应用程序如何使用驱动程序
  10. GNU C++ 智能指针4-- 解析_Sp_counted_ptr类