实验三 打造自己的图像识别模型

实验编号:BQP-A-EasyHBase

实验类别 实验

实验学时 3.0

一、实验目的

通过之前对Mnist、CIFAR-10数据库的操作使得学生初步了解了Tensorflow中的数据读取原理以及图像识别模型构建的基本架构。本次实验通过对已经预训练好的ImageNet模型进行微调(Fine-une)来使学生了解如何使用TensorFlow在自己的图像数据上训练深度学习模型。整个过程中能够培养学生动手能力,使学生提高综合能力,最终达以下到教学目标:

  1. 掌握微调的原理;
  2. 了解在Tensorflow中微调模型的方法;

二、实验原理

在自己的数据集上训练一个新的深度学习模型时,一般采取在预训练ImageNet上进行微调的方法。微调的原理以VGG16为例进行讲解。

图3-1 VGG16 结构示意图

如图3-1所示,VGG16的结构为卷积+全连接层。卷积层分为5个部分共13层,即图中的conv 1-conv 5。还有3层是全连接层,即图中的fc6、fc7、fc8。卷积层加上全连接层合起来一共为16层,因此它被称为VGG16。fc8层的输入是fc7层的特征,输出是1000类的概率,这1000类正好对应了ImageNet模型中的1000个类别。在自己的数据中,类别数一般不是1000类,因此fc8层的结构在此时是不适用的,必须将fc8层去掉,用符合数据集类别数的全连接层,作为新的fc8。比如数据集为5类,那么新的fc8的输出也应当是5类。
       在ImageNet数据集上训练过的VGG16中的参数已经包含了大量有用的卷积过滤器,因此在训练时,网络的参数的初始值并不是随机化生成的,而是采用VGG16在ImageNet上已经训练好的参数作为训练的初始值。这样做不仅可以节约大量训练时间,而且有助于分类器性能的提高。
       载入VGG16的参数后,就可以开始训练了。此时需要指定训练层数的范围。一般来说,可以选择以下几种范围进行训练:
       第一种:只训练fc8。保持其他层的参数不动。此时VGG16当作类似于一个“特征提取器”,用fc7层提取的特征做一个Softmax模型分类。这样做的好处是训练速度快,但往往性能不会太好。
       第二种:训练所有参数。还可以对网络中的所有参数进行训练,这种方法的训练速度可能比较慢,但是能取得较高的性能。

第三种:训练部分参数。通常是固定浅层参数不变,训练深层参数。如固定conv1、conv2部分的参数不训练,只训练conv 3、conv4、conv 5、fc6、fc7、fc8的参数。
    这种训练方法就是对神经网络模型做微调。借助微调,可以从预训练模型出发,将神经网络应用到自己的数据集上。

三、实验环境

虚拟机数量:3台

操作系统:Ubuntu 14.04

实验环境及版本:

Hadoop:Hadoop 2.2.0

Java:java version "1.7.0_51"

四、实验内容及步骤

1.准备工作

(1)Tensorflow环境的搭建:

必须可以正常启动Tensorflow环境,确保Tensorflow能够上传和读写文件。

  1. 数据准备:
      本次实验的数据来源于自己的数据集,但是在导入数据之前,我们必须先将数据划分为训练数据集和验证数据集。训练集用于训练模型,验证集用于验证模型的准确度。本次实验采用的是卫星图像数据集,该数据集一共有六个类别。具体见表3-1.

类别名

含义

示例图像

Wetland

农田

Glacier

冰川

Urban

城市区域

Rock

岩石

water

水域

Wood

森林

表3-1 卫星数据的类别及图像实例

这些原始的图像数据均保存在pic文件夹中。现在我们需要将这些原始数据分为train和validation两个目录,分别表示训练使用的图片和验证使用的图片。在每个目录中,分别以类别名为文件夹名保存所有图像。在每个类别文件夹下,存放的就是原始的图像(如jpg格式的图像文件)。之后使用预先编制好的脚本data_convert.py,将图片转换为为tfrecord格式,在命令行内输入如下代码。

python data_convert.py -t pic/ \

--train-shards 2 \

--validation-shards 2 \

--num-threads 2 \

--dataset-name satellite

-t pic/: 表示转换pic文件夹中的数据。pic文件夹中必须有一个train目录和一个validation目录,分别代表训练和验证数据集。每个目录下按类别存放了图像数据。

--train-shards 2 \中的2是指将训练数据集分为两块,即最后的训练数据就是两个tfecord格式的文件。如果读者的数据集较大,可以考虑将其分为更多的数据块。

-num-threads 2中的2是指采用两个线程产生数据。为了使得每个线程处理的数据块数是相同的,线程数必须要能整除train-shards和validation-shards。

--dataset-name satellite给生成的数据集起一个名字。这里将数据集起名satellite,最后生成文件的开头就是satellite_train和satellite_validation。

图3-2 下载后产生的新文件

运行完命令后会在pic文件夹内生成5 个新的文件(如图3-2所示),其包括训练数据satellite_train_00000-of-00002.tfrecord,satellite_train_00001-of-00002.tfrecord,以及验证数据satellite_validation_00000-of-00002.tfrecord ,satellite_validation _00001-of-00002 .tfrecord 。另外,还有一个文本文件label.txt ,表示图片的内部标签(数字)到真实类别(字符串)之间的映射顺序。如图片在tfrecord 中的标签为0 ,那么就对应label.txt 第一行的类别,在tfrecord的标签为1 ,就对应label.txt 中第二行的类别,依此类推。

注意:由于python版本的不同,在python3.0及以上的版本上运行以上的命令可能会报错(如下图3-3)。

图3-3 版本不同而报错的情况

解决方法是在tfrecord.py文件中做如下几处做更改,更改后即可正常运行:

//第一
def _bytes_feature(value):
"""Wrapper for inserting bytes features into Example proto."""
value=tf.compat.as_bytes(value)//这行需要添加   
return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value]))

//第二
def _process_image(filename, coder):
with open(filename, 'rb') as f://这里需要加个b

image_data = f.read()

//第三
xrange需要都改为range

//第四
_find_image_files:
shuffled_index = list(range(len(filenames)))//这里加上了list   (python3中range不返回数组对象,而是返回range对象)

2.使用TensorFlow Slim微调模型

TensorFlow Slim 是Google 公司公布的一个图像分类工具包,它不仅定义了一些方便的接口,还提供了很多ImageNet数据集上常用的网络结构和预训练模型。首先我们应该明白如何下载Slim 的源代码,再了解如何在Slim 中定义新的数据库以及如何使用新的数据库训练、如何进行参数调整等问题。

2.1 下载TensorFlow Slim的源代码

如果需要使用Slim 微调模型,首先要下载Slim的源代码。Slim的源代码保存在tensorflow/models 项目中,可以使用下面的git命令下载ensorflow/models:

git clone https://github.com/tensorflow/models.git

找到models/research/ 目录中的slim文件夹,这就是要用到的TensorFlow Slim 的源代码。TensorFlow Slim的代码结构见表3-2。

文件夹或文件名

用途

datasets

定义一些训练中使用的数据集,预先设定的有mnist等数据集,如果有像训练自己的数据集需要在datasets中进行定义

nets/

定义了一些常见的网络结构,如AlexNet等

processing/

定义了一些预处理方法

scripts

包含一些实例脚本

train_image_classifier.py

训练模型的入口代码

eval_image_classifier.py

验证模型性能的入口代码

download_and_convert_data.py

下载站还数据集格式的入口代码

表3-2 TensorFlow Slim的代码结构

2.2定义新的datasets文件

首先,在datasets/目录下新建一个文件satellite.py,并将flowers.py 文件中的内容复制到satellite.py 中。接下来,需要修改以下几处内容:第一处是FILE_PATTERN 、SPLITS_TO SIZES 、NUM_CLASSES , 将其进行以下修改:

FILE_PATTERN = 'satellite_%s_*.tfrecord'

SPLITS_TO_SIZES = {'train':4800, 'validation':1200}

_NUM_CLASSES = 6

FILE_PATTERN变量定义了数据的文件名的格式和训练集、验证集的数量。这里定义_FILE_PATTERN = ‘satellite%s_*.tfrecord’和SPLITS_TO_SIZES={‘train’:4800, ‘validation’:1200},表明数据集中,训练集的文件格式为satellite_train_*.tfrecord共包含4800张图片,验证集文件名格式为satellite_validation_*.tfrecord,共包含1200张图片。_NUM_CLASSES变量定义了数据集中图片的类别数目。

第二处修改image/format部分,将之修改为:

'image/format' tf.FixedLenFeature( (), tf. string, default_value ='jpg'),

此处定义了图片的默认格式。收集的卫星图片的格式为jpg图片,因此修改为jpg 。修改完satellite.py后,还需要在同目录的dataset_factory.py文件中注册satellite数据库。未修改的dataset_factory. py 中注册数据库的对应代码为:

from datasets import cifar10

from datasets import flowers

from datasets import imagenet

from datasets import mnist

from datasets import satellite

datasets_map = { 'cifar10': cifar10, 'flowers': flowers, 'imagenet': imagenet, 'mnist': mnist, 'satellite': satellite, }

2.3 准备训练文件夹

定义完数据集后,在slim文件夹下再新建一个satellite目录,在这个目录中,完成最后的几项准备工作:

新建一个data目录,并将转换好格式的训练数据复制进去。

新建一个空的train_dir 目录,用来保存训练过程中的日志和模型。

新建一个pretrained目录,在slim的GitHub页面找到Inception V3 模型的下载地址,下载并解压后,会得到一个inception_v3 .ckpt 文件,将该文件复制到pretrained 目录下。

2.4训练模型

在slim 文件夹下,运行以下命令就可以开始训练了:

python train_image_classifier.py \

--train_dir=satellite/train_dir \

--dataset_name=satellite \

--dataset_dir=satellite/data \

--model_name=incepttion_v3 \

--checkpoint_path=satellite/pretrained/inception_v3.ckpt \

--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \

--trainable_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \

--max_number_of_strps=100000 \

--batch_size=32 \

--learning_rate=0.001 \

--learning_rate_decay_type=fixed \

--save_interval_secs=300 \

--save_summaries_secs=2 \

--log_every_n_steps=10 \

--optimizer=rmpsprop \

--weight_decay=0.00004

上述命令的参数较多,下面一一做出解释:

--trainable_scopes=InceptionV3/Logits,InceptionV3/AuxLogits:首先来解释trainable_scope的作用,trainable_scopes规定了在模型中微调变量的范围。这里的设定表示只对InceptionV3/Logits,InceptionV3/AuxLogits 两个变量进行微调,其它的变量都不动。InceptionV3/Logits,InceptionV3/AuxLogits就相当于在第一节中所讲的fc8,他们是Inception V3的“末端层”。如果不设定trainable_scopes,就会对模型中所有的参数进行训练。

--train_dir=satellite/train_dir:表明会在satellite/train_dir目录下保存日志和checkpoint。

--dataset_name=satellite、–dataset_split_name=train:指定训练的数据集。在3.2节中定义的新的dataset就是在这里发挥用处的。

--dataset_dir=satellite/data: 指定训练数据集保存的位置。

--model_ name=inception_v3 :使用的模型名称。

--checkpoint_path=satellite/pretrained/inception_v3.ckpt:预训练模型的保存位置。

--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits : 在恢复预训练模型时,不恢复这两层。正如之前所说,这两层是InceptionV3模型的末端层,对应着ImageNet 数据集的1000 类,和当前的数据集不符,因此不去恢复它。

--max_number_of_steps 100000 :最大的执行步数。

--batch_size =32 :每步使用的batch 数量。

--learning rate=0.001 : 学习率。

--learning_rate_decay_type=fixed:学习率是否自动下降,此处使用固定的学习率。

--save_interval_secs=300 :每隔300s 程序会把当前模型保存到train_dir中。此处就是目录satellite/train_dir 。

–save_summaries_secs=2 :每隔2s就会将日志写入到train_dir中。可以用TensorBoard查看日志。此处为了方便观察,设定的时间间隔较多,实际训练时为了性能考虑,可以设定较长的时间间隔。

--log_every_n_steps=10: 每隔10 步,就会在屏幕上打出训练信息。

--optimizer=rmsprop: 表示选定的优化器。

--weight_decay=0.00004 :选定的weight_decay值。即模型中所高参数的二次正则化超参数。

以上命令是只训练末端层InceptionV3/Logits, InceptionV3 /AuxLogits, 还可以使用以下命令对所有层进行训练:

python train_image_classifier.py \

--train_dir=satellite/train_dir \

--dataset_name=satellite \

--dataset_dir=satellite/data \

--model_name=incepttion_v3 \

--checkpoint_path=satellite/pretrained/inception_v3.ckpt \

--checkpoint_exclude_scopes=InceptionV3/Logits,InceptionV3/AuxLogits \

--max_number_of_strps=100000 \

--batch_size=32 \

--learning_rate=0.001 \

--learning_rate_decay_type=fixed \

--save_interval_secs=300 \

--save_summaries_secs=2 \

--log_every_n_steps=10 \

--optimizer=rmpsprop \

--weight_decay=0.00004

对比只训练、末端层的命令,只再一处发生了变化,即去掉了--trainable_ scopes 参数。原先的–trainable_ scopes= Inception V3 /Logits ,InceptionV3 / AuxLogits 表示只对末端层Inception V3 /Logits 和Inception V3 / AuxLogits 进行训练,去掉后就可以训练模型中的所有参数了。

程序具体运行过程如下。启动train_ image_ classifier. py 程序,如果训练文件夹(即satellite/train_ dir )里没有已经保存的模型,就会加载checkpoint_path中的预训练模型,紧接着,程序会把初始模型保存到train_dir中,命名为model.cpkt-0,0表示第0步。这之后,每隔5min(参数一save interval secs=300 指定了每隔300s 保存一次,即5min)。程序还会把当前模型保存到同样的文件夹中,命名格式和第一次保存的格式一样。因为模型比较大,程序只会保留最新的5 个模型。

此外,**如果中断了程序井再次运行,程序会首先检查train dir 中有无已经保存的模型,如果有,就不会去加载checkpoint_path中的预训练模型, 而是直接加载train dir 中已经训练好的模型,并以此为起点进行训练。**Slim之所以这样设计,是为了在微调网络的时候,可以方便地按阶段手动调整学习率等参数。

2.5 验证模型准确率

我们用eval_image classifier.py 程序进行验证模型的准确率。执行下列命令:

Python eval_image classifier.py \

--checkpoint_path=satellite/train _ dir \

--eval_dir=satellite/eval_dir

--dataset_name=satellite

--dataset_split_name=validation

--dataset_dir=satellite/data

--model_ name=inception_ v3

命令中所涉及到的参数解释如下:

--checkpoint_path=satellite/train _ dir: 这个参数既可以接收一个目录的路径,也可以接收一个文件的路径。如果接收的是一个目录的路径,如这里的satellite/train_dir就会在这个目录中寻找最新保存的模型文件,执行验证。也可以指定一个模型验证,以第300步为例,在satellite/train_ dir 文件夹下它被保存为model.clcpt-300.meta, model.ckpt-300.index ,model. ckpt-3 00.data-00000-of-00001 三个文件。此时,如果要对它执行验证,给checkpoint_path 传递的参数应该为satellite/train_ dir/model.ckpt-300 。

--eval_dir=satellite/eval_dir :执行结果的日志就保存在eval_dir 中,可以通过TensorBoard查看。

--dataset_name=satellite,--dataset_split_name=validation 指定需要执行的数据集。注意此处是使用验证集( validation )执行验证。

--dataset_dir=satellite/data :数据集保存的位置。

--model_ name=inception_ v3 :使用的模型名称。

运行完命令后出现出现如下运行结果:

Accuracy表示模型的分类准确率,而Recall_5 表示Top 5 的准确率,即在输出的各类别概率中,正确的类别只要落在前5 个就算对。由于此处的类别数比较少,因此可以不执行Top 5 的准确率,民而执行Top 2 或者Top 3的准确率,只要在eval_image_classifier.py 中修改下面的部分就可以了:

names_to_values, names_to_updates = slim.metrics.aggregate_metric_map({

'Accuracy': slim.metrics.streaming_accuracy(predictions, labels),

'Recall_5': slim.metrics.streaming_recall_at_k(logits, labels, 5),})

  1. 导出模型并对单张图片进行识别

一般我们将模型训练完后会部署训练好的模型并对单张图片做识别。TensorFlow Slim提供了导出网络结构的脚本export_inference_ graph.py 。首先在slim 文件夹下运行:

python export_inference_ graph.py \

--alsologtostderr \

--model_name=inception_v3  \

--output_file=satallite/inception_v3_inf_graph.pb \

--dataset_name satellite

这个命令会在satellite 文件夹中生成一个inception_v3 _inf _graph. pb 文件。注意: inception_v3 _inf _graph.pb 文件中只保存了Inception V3 的网络结构,并不包含训练得到的模型参数,需要将checkpoint 中的模型参数保存进来。方法是使用freeze_graph. py 脚本(在chapter_3 文件夹下运行),命令如下所示:

python freeze-graph.py \

--input_graph slim/satellite/inception_v3_inf_graph.pb \

--input_checkpoint slim/satallite/train_dir/model.ckpt-5271 \

--input_binary true \

--output_node_names InceptionV3/Predictions/Reshape_1 \

--output_graph slim/satellite/frozen_graph.pb

这里参数含义为:

–input_graph slim/satellite/inception_v3_inf_graph.pb。这个参数很好理解,它表示使用的网络结构文件,即之前已经导出的inception_v3 _inf_gr aph.pb 。

–input_checkpoint slim/satallite/train_dir/model.ckpt-5271。具体将哪一个checkpoint 的参数载入到网络结构中。这里使用的是训练文件夹train _d让中的第5271 步模型文件。我们需要根据训练文件夹下checkpoint的实际步数,将5271修改成对应的数值。

input_binary true。导入的inception_v3_inf_graph.pb实际是一个protobuf文件。而protobuf 文件有两种保存格式,一种是文本形式,一种是二进制形式。inception_v3 _ inf graph. pb 是二进制形式,所以对应的参数是–input binary true 。初学的话对此可以不用深究,若高兴趣的话可以参考资料。

–output_graph slim/satellite/frozen_graph.pb。最后导出的模型保存为slim/satellite /frozen_graph.pb 文件。

如何使用导出的frozen_graph.pb 来对单张图片进行预测?编写了一个classify image_inception_ v3.py 脚本来完成这件事。首先来看这个脚本的实现方式。代码中包含一个preprocess for_ eval函数,它实际上是从slim/preprocessing/inception_preprocess ing.py里复制而来的,用途是对输入的图片做预处理。classify_ image_inception_v3.py 的主要逻辑在run_inference_on_ image函数中,第一步就是读取图片,并用preprocess_for_eval做预处理:

with tf.Graph().as_default():

image_data = tf.gfile.FastGFile(image, 'rb').read()

image_data = tf.image.decode_jpeg(image_data)

image_data = preprocess_for_eval(image_data, 299, 299)

image_data = tf.expand_dims(image_data, 0)

with tf.Session() as sess:

image_data = sess.run(image_data)

Inception V3 的默认输入为299 * 299 ,所以调用preprocess_for_eval 时指定了宽和高都是299 。接着调用create_graph()将模型载入到默认的计算图中。

def create_graph():

"""Creates a graph from saved GraphDef file and returns a saver."""

with tf.gfile.FastGFile(FLAGS.model_path, 'rb') as f:

graph_def = tf.GraphDef()

graph_def.ParseFromString(f.read())

_ = tf.import_graph_def(graph_def, name='')

FLAGS.model_path 就是保存的slim/satellite/frozen_graph.pb 。将之导入后先转换为graph_def,然后用tf.import_graph_def()函数导入。导入后,就可以创建Session 并测试图片了,对应的代码为:

with tf.Session() as sess:

softmax_tensor = sess.graph.get_tensor_by_name('InceptionV3/Logits/SpatialSqueeze:0')

predictions = sess.run(softmax_tensor,{'input:0': image_data})

predictions = np.squeeze(predictions)

node_lookup = NodeLookup(FLAGS.label_path)

top_k = predictions.argsort()[-FLAGS.num_top_predictions:][::-1]

for node_id in top_k:

human_string = node_lookup.id_to_string(node_id)

score = predictions[node_id]

print('%s (score = %.5f)' % (human_string, score))

InceptionV3/Logits/SpatialSqueeze:0是各个类别Logit值对应的节点。输入预处理后的图片image_data,使用sess.run()函数去除各个类别预测Logit。默认只取最有可能的FLAGS.num_top_predictions个类别输出,这个值默认是5。可以运行脚本时用–num_top_predictions参数来改变此默认值。node_ lookup 定义了一个NodeLookup 类,它会读取label文件,并将模型输出的类别id转换成实际类别名。

之后我们来看这个脚本应该如何使用:

python classify_image_inception_v3.py \

--model_path slim/satellite/frozen_graph.pb \

--label_path data_prepare/pic/label.txt \

--image_file test_image.jpg

--model_path就是之前导出的模型frozen_graph. pb 。模型的输出实际是“第0 类’、“第1 类”……所以用--label_path 指定了一个label文件,label文件中按顺序存储了各个类别的名称,这样脚本就可以把类别的id号转换为实际的类别名。–image _file 是需要测试的单张图片。脚本的运行结果类似于:

这就表示模型预测图片对应的最可能的类别是water,接着是wetland 、urban 、wood 等。score 是各个类别对应的Logit 。

  1. 实验总结

本次实验通过使用TensorFlow Slim 微调预训练模型,包括数据准备、定义新的datasets文件、训练、验证、导出模型井测试单张图片等,让学生掌握微调神经网络的基本原理以及了解图像识别模型在训练时具体的运行过程。通过本章的学习让学生可以修改对应的代码来训练自己的数据,来打造自己的图像识别模型。这有助于激发学生的独立思考能力和动手实践能力。

打造自己的图像识别模型相关推荐

  1. 打造自己的图像识别模型2——使用 TensorFlow Slim 微调模型

    最近在搞自己的深度学习图像识别模型.这是第二步 本文主要讲解在现有常用模型基础上,如何微调模型,减少训练时间,同时保持模型检测精度. 主要参考书籍<21个项目玩转深度学习:基于TensorFlo ...

  2. 不需要程序员,产品经理也能10分钟打造一个深度学习模型

    想要给产品加上图像识别或声音识别的AI能力需要做哪些工作? 第一步:定义和明确需求,第二步:采集和标注大量的数据,第三步:写算法.调参数,第四步:校验模型效果并持续反复第二步和第三步,第五步:将模型做 ...

  3. 【项目实战课】微信小程序图像识别模型前后端部署实战

    欢迎大家来到我们的项目实战课,本期内容是<微信小程序图像识别模型前后端部署实战>.所谓项目实战课,就是以简单的原理回顾+详细的项目实战的模式,针对具体的某一个主题,进行代码级的实战讲解. ...

  4. CVPR 2019 | 旷视研究院提出ML-GCN:基于图卷积网络的多标签图像识别模型

    全球计算机视觉三大顶会之一 CVPR 2019 (IEEE Conference on Computer Visionand Pattern Recognition)将于 6 月 16-20在美国洛杉 ...

  5. 让AI学习如何玩游戏 OpenAI打造强化学习通用模型

    过度学习是许多AI模型的问题,要开发出通用型AI,就必须解决这道难题,OpenAI近日释出一个特别的训练环境CoinRun,该平台藉由游戏设计,能够提供指针,让程序将过去学习到的经验,转移到全新环境中 ...

  6. 迁移学习训练集准确率一直上不去_可以提高你的图像识别模型准确率的7个技巧...

    假定,你已经收集了一个数据集,建立了一个神经网络,并训练了您的模型. 但是,尽管你投入了数小时(有时是数天)的工作来创建这个模型,它还是能得到50-70%的准确率.这肯定不是你所期望的. 下面是一些提 ...

  7. 利用模型算法部署图像识别_利用这些技巧增强您的图像识别模型

    利用模型算法部署图像识别 So, you have gathered a dataset, built a neural network, and trained your model. 因此,您已经 ...

  8. 人工智能肺炎图像识别模型-企业模型_科研机构应用

    什么是肺炎? 肺炎是一种肺部炎症,主要影响称为肺泡的小气囊. alveoli with fluid 有液体肺泡(病态) health alveoli 健康的肺泡 blockage of the bro ...

  9. 基于深度学习的图像识别模型研究综述

    基于深度学习的图像识别模型研究综述 摘要:深度学习是机器学习研究中的一个新的领域,其目的在于训练计算机完成自主学习.判断.决策等人类行为并建立.模拟人脑进行分析学习的神经网络,它模仿人类大脑的机制来解 ...

最新文章

  1. (五)Maven目录结构及常用命令说明
  2. 将 Shiro 作为应用的权限基础 五:密码的加密/解密在Spring中的应用
  3. Weka学习四(属性选择)
  4. 简单排序--插入排序
  5. 史上最经典CAP讲解
  6. wordpress ?php the_time() ?,WordPress时间函数the_time与get_the_time解析
  7. python 批量自动搜索、自动抓取需要的信息简单教程【selenium】
  8. iOS 不错的第三方开源库归类分享
  9. 河北对口计算机计算机网络,河北对口计算机试题及答案.doc
  10. Latex参考文献的代码
  11. 战舰 Linux服务端,战舰帝国一键端手游服务端+GM工具+视频教程
  12. Squitd代理服务器安装
  13. j1900适合装哪版群晖_4000-10000元适合家庭使用的台式电脑选购指南(9月)
  14. 三、数据特性计算(含信道通讯计算、码元速率计算、尼奎斯特定理、香农定理以及相应的练习题)——网络工程师成长之路
  15. C语言中access函数
  16. 将多个列重新排版成交叉表
  17. java web 登录_javaWeb实现登录功能
  18. c语言判断真假,C语言中的真假值
  19. 游戏感:虚拟感觉的游戏设计师指南——第十五章 超级马里奥64
  20. 神经网络初认识——BP神经网络(7月18,再次认识)

热门文章

  1. 一行代码实现蒲公英市场APP检查更新
  2. Spring Boot实践 | 利用Spring Security快速搞定权限控制
  3. 转载 | 年度盘点!Flink 社区全年的精华内容都在这里啦
  4. 图片如何转PDF?这两种方法很好用
  5. 2020-03-03学习日结
  6. 如何解决app store显示invalid address的问题
  7. 宏碁笔记本一键重装win7系统教程
  8. Mac下Tesseract-OCR文字识别新手使用入门
  9. 小米手环8和苹果S8哪款好 小米手环8和苹果S8区别
  10. html图片的隐藏与显示,Jquery中使用show()与hide()方法动画显示和隐藏图片