助力软件开发企业降本增效 PHP / java源码系统,只需一次付费,代码终身使用! 广告
# DNN实例实现 为什么要在卷积神经网络里加上一节DNN呢?为了以下目的 1. 为了熟悉tensorflow的使用、 2. 为下面实现卷积神经网络的具体实现做铺垫、 **我们先从tensorflow官网的例子入手,来实现一个简单的DNN**。(以目的为导向的学习方式我认为是最有效的,所以以后我们就一边实现具体的实例一边去学习具体的知识点,因为单独去看知识点太枯燥了) 首先我建议首先看一下官网的该例子(是中文的不用害怕,第一遍肯定是蒙的不要灰心,先跟着我的教程敲一遍,回头再看,你就会有点感觉了):https://www.tensorflow.org/get_started/get_started_for_beginners 我现在假设你已经看过一遍该例子了,同时你必须有python的基础知识,如果没有我建议你去看一遍这个教程:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 好了下面我就带领大家一步步实现例子中的代码,虽然没有官网上讲的清楚但是都是你可以理解的: 废话少说我们开始实现: 首先该例子是:**鸢尾花分类问题** **问题**:我们将仅根据鸢尾花花萼和花瓣的长度和宽度对其分为三类:山鸢尾 维吉尼亚鸢尾 。为了预测看到的鸢尾花究竟属于哪一类。 **条件**:我们已经创建了一个包含 120 株鸢尾花的数据集,数据集前5个条目如下: | 花萼长度 | 花萼宽度 | 花瓣长度 | 花瓣宽度 | 品种 | | --- | --- | --- | --- | --- | | 6.4 | 2.8 | 5.6 | 2.2 | 2 | | 5.0 | 2.3 | 3.3 | 1.0 | 1 | | 4.9 | 2.5 |4.5 | 1.7 | 2 | | 4.9 | 3.1 | 1.5 | 0.1 | 0 | | 5.7 | 3.8 | 1.7 | 0.3 | 0 | * 0 代表 setosa(山鸢尾) * 1 代表 versicolor(变色鸢尾) * 2 代表 virginica(维吉尼亚鸢尾) **方法**:我们首先构建DNN(神经网络)然后训练该网络,然后就可以使用训练好的网络进行预测。网络结构类似如下图所示: :-: ![](https://box.kancloud.cn/3090b771954aef5ec25cf4841e252b00_667x535.png) 知道了具体问题和解决问题的方法我们下一步就要用代码去实现,首先要确定你已经安装好了环境如果没有请参考前面环境搭建一章。 首先打来Anaconda Prompt(一个命令行窗口): 输入以下命令来创建一个新的开发环境,因为我已经有一个tensorflow的开发环境了所以我现在使用tensorflow1同时也为了让大家能很容易的实现该例子: ~~~ conda create --name tensorflow1 ~~~ 输入下面命令进去该环境: ~~~ conda activate tensorflow1 ~~~ 然后我们输入以下命令可以看到该环境都安装了那些包,然后我们看到该环境什么都没有: ~~~ conda list ~~~ 好了我们开始安装软件包: 先安装python我们选择目前最新的版本,把依赖的也都安装下来选择y(有兴趣可以看看 conda 和 pip 包管理工具的不同,其实主要就一点:是否安装所依赖的包) ~~~ conda install python=3.6.5 ~~~ 然后输入以下命令安装panda,因为该实例需要用到。(这一次我们用pip安装是为了不让它出现所依赖的包安装提醒) ~~~ pip install pandas ~~~ 最后我们安装tensorflow: ~~~ pip install --upgrade --ignore-installed tensorflow ~~~ 测试tensorflow是否安装成功: 输入以下代码: 输入以下命令进入python交互环境: ~~~ python ~~~ 然后输入以下代码 ~~~ # python code import tensorflow as tf hello = tf.constant('Hello, TensorFlow!') sess = tf.Session() print(sess.run(hello)) ~~~ 按一下enter键可以看到以下就证明安装成功: :-: ![](https://box.kancloud.cn/134d927a194a917e58040a0c737aed44_677x458.png) **好的环境搭建好了我们使用编辑器开始写代码吧**! 打开pycharm 点击file---->project然后输入项目的名字:dnn,选择new environment using 如下图所示: ![](https://box.kancloud.cn/c836027fd1e9b7f810914aa0e37270c8_783x488.png) 然后去选择你刚才新建的环境中的Python3.6.5的解析器(应该在envs\tensorflow1下面) 如下图过程: ![](https://box.kancloud.cn/dda5dbfe9c837b1db965a405f758a1ee_783x488.png) ![](https://box.kancloud.cn/df97efc952b23d63af9915ce671d994f_854x629.png) ![](https://box.kancloud.cn/f7f574db80b1d15603d6e75689798d2b_839x568.png) 然后点击create。 首先我们在dnn下建立一个python包名叫DNNclassfier,如下图 ![](https://box.kancloud.cn/6ce1f2632e7c961b0b2c7b40694f17e3_730x613.png) 然后我们在包DNNclassfier下创建名为:premade_estimator.py 然后输入tensorflow项目的架子代码: ~~~ from __future__ import absolute_import #为了解决python版本不一致的问题 from __future__ import division #为了解决python版本不一致的问题 from __future__ import print_function #为了解决python版本不一致的问题 import tensorflow as tf def main(argv): #主要代码 if __name__ == '__main__': tf.logging.set_verbosity(tf.logging.INFO) #设置日志记录类型 tf.app.run(main) #执行代码 ~~~ 要用120 株鸢尾花的数据集进行网络训练我们首先要把数据读入网络,所以我们单独建立一个模块来操作数据,新建一个iris_data.py文件。 输入以下代码(不解释了注释写的很明白了): ~~~ #导入所依赖的模块 import pandas as pd import tensorflow as tf #定义所需要的常量, csv是一种文件格式 TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv" TEST_URL = "http://download.tensorflow.org/data/iris_test.csv" # 定义数据集列的名字 CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species'] #定义三个类别常量 SPECIES = ['Setosa', 'Versicolor', 'Virginica'] ~~~ 然后接着在iris_data.py文件中输入下面代码: ~~~ # 读取网上文件到本地,Keras 是一个开放源代码机器学习库;tf.keras 是 Keras 的一种 TensorFlow 实现。 def maybe_download(): train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL) test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL) return train_path, test_path #测试代码 if __name__ == '__main__': x, y = maybe_download() print(x) print(y) ~~~ 然后运行 iris_data.py会出现: :-: ![](https://box.kancloud.cn/cea90ed5c789fe5f4255eac921c9a826_1182x277.png) 这就是文件下载下来所放的位置。 好了数据集文件下载下来了我们来把它读取到内存。 接着在iris_data.py文件中定义load_data函数代码如下: ~~~ def load_data(y_name = 'Species'): """返回数据集如(train_x, train_y), (test_x, test_y)格式""" #获取下载下来数据集的本地路径 train_path, test_path = maybe_download(); # 以pandas中定义列表数据结构DataFrame表示在内存中 train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0) train_x, train_y = train, train.pop(y_name) test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0) test_x, test_y = test, test.pop(y_name) return (train_x, train_y), (test_x, test_y) ~~~ 你可以添加测试代码进行测试看返回的结果是什么(这里我就不截图了因为数据量有点大): ~~~ #测试代码 if __name__ == '__main__': x, y = load_data() print(x) print(y) ~~~ 到目前我们已经可以把数据集以DataFrame数据结构形式读入到内存。我们可以开始在premade_estimator.py写主要代码了: 在premade_estimator.py中输入下面代码: ~~~ def main(): #读取数据 (train_x, train_y), (test_x, test_y) = iris_data.load_data() #下面我们把读入内存的pandas数据格式DataFrame,换成一种模型能理解的格式,也就是所谓的特征列 #可以参考网址去深入了解:https://www.tensorflow.org/get_started/feature_columns my_feature_columns = [] for key in train_x.keys(): my_feature_columns.append(tf.feature_column.numeric_column(key=key)) ~~~ 你可以测一下my_feature_columns里面到底是什么东西,添加测试代码,如下 ~~~ if __name__ == '__main__': main() ~~~ 然后打断点,然后执行,我的结果是: :-: ![](https://box.kancloud.cn/bb4f87c3e9f941a5e8d460ac8d220e3a_1891x948.png) 好了,以后不清楚生成的变量是什么都这样去看看。不再赘述。 我们接着写代码,下面数据准备好以后开始搭建DNN网络,所幸的是tensorflow已经为我们写好了DNN实现的estimator,刚开始你不是很清楚estimator是什么其实就是一个tensorflow实现深度学习的一个架子,你可以使用它里面实现好了的estimator也可以自己实现estimator。这一节我们使用tensorflow里面实现好了的,下一届我们实现我们自己是卷积神经网络。 > 注意:Estimator的核心想法就是:把工作网络封装成一个类,训练、评估、预测都是类方法 > 在这种封装里面,首先隐藏了网络结构,对于程序运行者,只需要考虑输入输出。同时,包含了对参数数据的保存、对训练状态的保存,使得训练过程可复现,可追溯。其数据的管理,交给了Dataset,在进一步了解Dataset后,二者的协作会使得编写tensorflow程序变得井井有条。 然后输入下面代码创建DNN: ~~~ # 创建两个都包含10个神经元的隐含层的DNN classifier = tf.estimator.DNNClassifier( #指定特层列告诉DNN该如何使用原始数据 feature_columns=my_feature_columns, #指定隐含层 hidden_units=[10, 10], #指定该网络分多少种类 n_classes=3 ) ~~~ > 注意:我们使用DNNClassifier中默认的参数(说明文档上写的很清楚:https://www.tensorflow.org/api_docs/python/tf/estimator/DNNClassifier ):例如,损失函数是交叉熵函数,激活函数是RELU,优化函数选择AdagradOptimizer(其实是一种梯度下降法的变种)要想更多的了解一下AdagradOptimizer可以参考:https://blog.csdn.net/tsyccnh/article/details/76769232 ::::::但是有一点权值是怎么初始化的我目前还不太清楚。可能官网还看到的遍数少吧。不过不影响我们下面的进行,后期会补上这一缺点。 > 到目前我们既构建好了网络,又把数据集的数据以DataFrame数据结构读入内存了。 但是我们看DNNClassifierAPI会发现,DNNClassifier的train,evaluate,predict的API的输入全部要求是A 'tf.data.Dataset' object或者A tuple。所以我们下面要做的就是给train evaluate,生成这些对象。 首先我们还是打开iris_data.py文件。继续在该文件中添加下面代码: ~~~ def train_input_fn(features, labels, batch_size): """为了训练写的输入函数""" #转变输入的数据为Dataset的对象 dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels)) #随机拖拽,重复和批生成数据。(其实就是搅拌样本使随机化,并随机组合生成一批样本) dataset = dataset.shuffle(1000).repeat().batch(batch_size) return dataset ~~~ 没每走一步我们测试一下,下面我们测试一下中间生成了什么并返回了什么。 添加测试代码: ~~~ if __name__ == '__main__': (train_x, train_y), (test_x, test_y) = load_data() # 我们使用1个批次来测试 z = train_input_fn(train_x, train_y,1) ~~~ 然后打断点,点击debug调试,查看情况,看的是不是很蒙,没关系我们只知道现在变成了train可以使用的数据类型就行后期再学习dataset 的用法: :-: ![](https://box.kancloud.cn/101ea459efbb6cff64467e8175051560_1914x994.png) 然后我们去写用于训练和预测的输入函数: 代码如下: ~~~ def eval_input_fn(features, labels, batch_size) """为了评估和预测写的输入函数""" features = dict(features) if labels is None: #证明是预测 inputs = features else: inputs = (features, labels) #转变数据格式 dataset = tf.data.Dataset.from_tensor_slices(inputs) # assert 和 is 用法自己查资料吧,网上到处都是 assert batch_size is not None, "batch_size must not be None" dataset = dataset.batch(batch_size) return dataset ~~~ 好的我有了输入数据现在去写train、evaluate、predict函数吧! 打开premade_estimator.py 添加下面代码: ~~~ #训练该模型设置训练1000次,每次100个样本 classifier.train( input_fn=lambda :iris_data.train_input_fn(train_x, train_y, 100), steps=1000 ) # 评估该模型 eval_result = classifier.evaluate( input_fn=lambda :iris_data.eval_input_fn(test_x, test_y,100) ) #把评估结果打印到控制台 print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result)) ~~~ 目前我们有了训练和评估代码, 但是还记得我们这个网络的目的吗?那就是预测。下面我们开始写预测代码: 接着在premade_estimator.py中写: ~~~ #从训练好的模型生成预测 #首先我们要有所预测的实例,我们先自己随便写一个 expected = ['Setosa', 'Versicolor', 'Virginica'] predict_x = { 'SepalLength': [5.1, 5.9, 6.9], 'SepalWidth': [3.3, 3.0, 3.1], 'PetalLength': [1.7, 4.2, 5.4], 'PetalWidth': [0.5, 1.5, 2.1], } #写预测函数 predictions = classifier.predict( input_fn=lambda: iris_data.eval_input_fn(predict_x,labels=None,batch_size=100) ) # 创建输出的模板,如果不懂可以参考python的格式化输出一节 template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"') ~~~ 现在我们最后写显示到控制台的代码: ~~~ for pred_dict, expec in zip(predictions, expected): class_id = pred_dict['class_ids'][0] probability = pred_dict['probabilities'][class_id] print(template.format(iris_data.SPECIES[class_id], 100 * probability, expec)) ~~~ 这段代码我就不解释了很容易理解,如果不理解用我教你们的测试方法看看里面的变量都是什么形式的。至此我们就基本上结束了。现在你可以去运行premade_estimator.py了如果能看到下面的界面证明你成功了。 :-: ![](https://box.kancloud.cn/c76a2a5dcfa00e8e5f2d76d9ef7eb2eb_1903x1012.png) 如果你细心的话会发现我们这里有某些地方跟官网不太一样呀,确实我们现在的batch_size和训练步骤都是写死的。 官网上是可以从命令行输入这些参数的。为了与官网保持一致我们也填上以下代码: ~~~ import argparse parser = argparse.ArgumentParser() parser.add_argument('--batch_size', default=100, type=int, help='batch size') parser.add_argument('--train_steps', default=1000, type=int, help='number of training steps') def main(argv): args = parser.parse_args(argv[1:]) 把里面用到的写死的ize和训练步骤换成args.batch_size和args.train_steps ~~~ 到此我们就介绍了。敲完一遍再回头看官网,我想应该会有点感觉了吧。最后我们这里把最终的代码给写出来。 premade_estimator.py ~~~ from __future__ import absolute_import #为了解决python版本不一致的问题 from __future__ import division #为了解决python版本不一致的问题 from __future__ import print_function #为了解决python版本不一致的问题 import argparse import tensorflow as tf import iris_data parser = argparse.ArgumentParser() parser.add_argument('--batch_size', default=100, type=int, help='batch size') parser.add_argument('--train_steps', default=1000, type=int, help='number of training steps') def main(argv): args = parser.parse_args(argv[1:]) #读取数据 (train_x, train_y), (test_x, test_y) = iris_data.load_data() #下面我们把读入内存的pandas数据格式DataFrame,换成一会模型能理解的格式,也就是所谓的特征列 #可以参考网址去深入了解:https://www.tensorflow.org/get_started/feature_columns my_feature_columns = [] for key in train_x.keys(): my_feature_columns.append(tf.feature_column.numeric_column(key=key)) # 创建两个都包含10个神经元的隐含层的DNN classifier = tf.estimator.DNNClassifier( #指定特层列告诉DNN该如何使用原始数据 feature_columns=my_feature_columns, #指定隐含层 hidden_units=[10, 10], #指定该网络分多少种类 n_classes=3 ) #训练该模型设置训练1000次,每次100个样本 classifier.train( input_fn=lambda :iris_data.train_input_fn(train_x, train_y, args.batch_size), steps=args.train_steps ) # 评估该模型 eval_result = classifier.evaluate( input_fn=lambda :iris_data.eval_input_fn(test_x, test_y, args.batch_size) ) #把评估结果打印到控制台 print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result)) #从训练好的模型生成预测 #首先我们要有所预测的实例,我们先自己随便写一个 expected = ['Setosa', 'Versicolor', 'Virginica'] predict_x = { 'SepalLength': [5.1, 5.9, 6.9], 'SepalWidth': [3.3, 3.0, 3.1], 'PetalLength': [1.7, 4.2, 5.4], 'PetalWidth': [0.5, 1.5, 2.1], } #写预测函数 predictions = classifier.predict( input_fn=lambda: iris_data.eval_input_fn(predict_x,labels=None,batch_size=args.batch_size) ) # 创建输出的模板,如果不懂可以参考python的格式化输出一节 template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"') for pred_dict, expec in zip(predictions, expected): class_id = pred_dict['class_ids'][0] probability = pred_dict['probabilities'][class_id] print(template.format(iris_data.SPECIES[class_id], 100 * probability, expec)) if __name__ == '__main__': tf.logging.set_verbosity(tf.logging.INFO) #设置日志记录类型 tf.app.run(main) #执行代码 ~~~ iris_data.py ~~~ #导入所依赖的模块 import pandas as pd import tensorflow as tf #定义所需要的常量, csv是一种文件格式 TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv" TEST_URL = "http://download.tensorflow.org/data/iris_test.csv" # 定义数据集列名字 CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species'] #定义三个类别常量 SPECIES = ['Setosa', 'Versicolor', 'Virginica'] # 读取网上文件到本地,Keras 是一个开放源代码机器学习库;tf.keras 是 Keras 的一种 TensorFlow 实现。 def maybe_download(): train_path = tf.keras.utils.get_file(TRAIN_URL.split('/')[-1], TRAIN_URL) test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL) return train_path, test_path def load_data(y_name = 'Species'): """返回数据集如(train_x, train_y), (test_x, test_y)格式""" #获取下载下来数据集的本地路径 train_path, test_path = maybe_download(); # 以pandas中定义列表数据结构DataFrame表示在内存中 train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0) train_x, train_y = train, train.pop(y_name) test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0) test_x, test_y = test, test.pop(y_name) return (train_x, train_y), (test_x, test_y) def train_input_fn(features, labels, batch_size): """为了训练写的输入函数""" #转变输入的数据为Dataset的对象 dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels)) #随机拖拽,重复和批生成数据。(其实就是搅拌样本使随机化,并随机组合生成一批样本) dataset = dataset.shuffle(1000).repeat().batch(batch_size) return dataset def eval_input_fn(features, labels, batch_size): """为了评估和预测写的输入函数""" features = dict(features) if labels is None: #证明是预测 inputs = features else: inputs = (features, labels) #转变数据格式 dataset = tf.data.Dataset.from_tensor_slices(inputs) # assert 和 is 用法自己查资料吧,网上到处都是 assert batch_size is not None, "batch_size must not be None" dataset = dataset.batch(batch_size) return dataset if __name__ == '__main__': (train_x, train_y), (test_x, test_y) = load_data() # 我们使用1个批次来测试 z = train_input_fn(train_x, train_y,2) print("end") ~~~ 最后我们给出两个链接是关于梯度下降和正则化的下一节会用到: 梯度下降:https://www.cnblogs.com/maybe2030/p/5089753.html 正则化:https://blog.csdn.net/sinat_29819401/article/details/60885679 softmax和交叉熵:https://blog.csdn.net/allenlzcoder/article/details/78591535