深度学习 手写字体识别
数据集介绍:
mnist数据集使用tensorflow封装好的数据(包含6000张训练数据,1000张测试数据),图片大小为28x28。
在神经网络的结构上,一方面需要使用激活函数去线性化。另一方面需要增加网络的层数以解决更加复杂的问题。
关于tensorflow中的几个函数:
1. tf.Variable()和 tf.get_variable()中的区别:
tf.Variable(): 创建一个变量,
tf.get_variable(): 创建或者获取一个变量,在用来创建变量时,和 tf.Variable()功能基本相同。
这两个函数不同的地方是,tf.Variable()函数,变量名称时可选的参数。tf.get_variable()变量名称是必填的参数,它会根据这个名称去创建或者获取变量。
(引用自:https://blog.csdn.net/weixin_38698649/article/details/80099822)
变量共享主要涉及两个函数:tf.variable() 和tf.get_variable();即就是必须要在tf.variable_scope的作用域下使用tf.get_variable()函数。这里用tf.get_variable( ) 而不用tf.Variable( ),是因为tf.get_variable( ) 拥有一个变量检查机制,会检测已经存在的变量是否设置为共享变量,如果已经存在的变量没有设置为共享变量,TensorFlow 运行到第二个拥有相同名字的变量的时候,就会报错。
两个创建变量的方式。如果使用tf.Variable() 的话每次都会新建变量。但是大多数时候我们是希望重用一些变量,所以就用到了get_variable(),它会去搜索变量名,有就直接用,没有再新建。
既然用到变量名了,就涉及到了名字域的概念。通过不同的域来区别变量名,毕竟让我们给所有变量都取不同名字还是很辛苦。这就是为什么会有scope 的概念。name_scope 作用于操作,variable_scope 可以通过设置reuse 标志以及初始化方式来影响域下的变量,因为想要达到变量共享的效果, 就要在 tf.variable_scope()的作用域下使用 tf.get_variable() 这种方式产生和提取变量. 不像 tf.Variable() 每次都会产生新的变量, tf.get_variable() 如果遇到了已经存在名字的变量时, 它会单纯的提取这个同样名字的变量,如果不存在名字的变量再创建.。
2. name_scope和variable_scope的区别
TF中有两种作用域类型
命名域 (name scope),通过tf.name_scope 或 tf.op_scope创建;
变量域 (variable scope),通过tf.variable_scope 或 tf.variable_op_scope创建;
这两种作用域,对于使用tf.Variable()方式创建的变量,具有相同的效果,都会在变量名称前面,加上域名称。
对于通过tf.get_variable()方式创建的变量,只有variable scope名称会加到变量名称前面,而name scope不会作为前缀。例如 print(v1.name) # var1:0
网络的结构如图所示:
tensorflow实现:
# encoding: utf-8 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data class TrainData: def __init__(self, data_path, batch_size=100): """ :param data_path: 训练数据路径 """ self.batch_size = batch_size self.mnist = input_data.read_data_sets(train_dir=data_path, one_hot=True) def display_data_info(self): """ 输出数据集的信息 :return: """ print("-"*40) print("training data size: {}".format(self.mnist.train.num_examples)) print("test data size: {}".format(self.mnist.test.num_examples)) print("validation data size: {}".format(self.mnist.validation.num_examples)) print("-"*40) def next_batch(self): """ 获取下一个batch的数据 经过处理后的图片为(28x28=784)的一维数组 :return: """ xs, ys = self.mnist.train.next_batch(self.batch_size) # 返回训练数据和标签 xs: [batch_size, 784] return xs, ys def validation_data(self): """ 返回验证集 :return: """ images, labels = self.mnist.validation.images, self.mnist.validation.labels return images, labels def test_data(self): """ 返回测试集 :return: """ images, labels = self.mnist.test.images, self.mnist.test.labels return images, labels class Model: def __init__(self, input_node, output_node, layer1_node, layer2_node, regularizer=False, moving_average=False): """ :param input_node: 输入数据的维度 :param output_node: 输出数据的维度 :param layer1_node: 隐藏层 :param layer2_node: 隐藏层 :param regularizer: L2正则化 :param moving_average: 滑动平均模型 """ self.input_node = input_node self.output_node = output_node self.layer1_node = layer1_node self.layer2_node = layer2_node # self.train_step = train_step self.regularizer = regularizer self.moving_average = moving_average self.moving_average_decay = 0.99 # 滑动平均衰减率 # self.batch_size = batch_size """ 关于滑动平均模型: 实际的原理是对参数的更新进行一阶之后滤波 对每一个参数会维护一个影子变量shadow_variable shadow_variable = shadow_variable * decay + (1 - decay) * variable decay 决定了模型的更心速度 num_updates参数来动态设置参数的大小 decay = min{decay, (1+num_updates)/(10+num_up_dates)} """ self.learning_rate_base = 0.0001 # 初始学习率 self.learning_rate_decay = 0.98 # 学习率的衰减速度 self.regularizer_rate = 0.001 # 正则化lambda with tf.name_scope("placeholder"): # 变量名 self.x = tf.placeholder(dtype=tf.float32, shape=[None, self.input_node], name='x-input') self.y_ = tf.placeholder(dtype=tf.float32, shape=[None, self.output_node], name='y-input') with tf.name_scope("weights"): # 网络第一层 self.weight1 = tf.get_variable(initializer=tf.random_normal(dtype=tf.float32, shape=[self.input_node, self.layer1_node], mean=0, stddev=1), name='weight1') self.biases1 = tf.get_variable(initializer=tf.constant_initializer(0.0), shape=[self.layer1_node], dtype=tf.float32, name='biases1') self.layer1 = tf.nn.relu(tf.matmul(self.x, self.weight1) + self.biases1) # 第一层 # 正则化 if self.regularizer: tf.add_to_collection(name='loss', value=tf.contrib.layers.l2_regularizer(self.regularizer_rate)(self.weight1)) # 网络第二层 self.weight2 = tf.get_variable(initializer=tf.random_normal(dtype=tf.float32, shape=[self.layer1_node, self.layer2_node], mean=0, stddev=1), name='weight2') self.biases2 = tf.get_variable(initializer=tf.constant_initializer(0.0), shape=[self.layer2_node], dtype=tf.float32, name='biases2') self.layer2 = tf.nn.relu(tf.matmul(self.layer1, self.weight2) + self.biases2) # 正则化 if self.regularizer: tf.add_to_collection(name='loss', value=tf.contrib.layers.l2_regularizer(self.regularizer_rate)(self.weight2)) # 网络第三层 全连接层 self.weight3 = tf.get_variable(initializer=tf.random_normal(dtype=tf.float32, shape=[self.layer2_node, self.output_node], mean=0, stddev=1), name='weight3') self.biases3 = tf.get_variable(initializer=tf.constant_initializer(0.0), shape=[self.output_node], dtype=tf.float32, name='biases3') self.y = tf.matmul(self.layer2, self.weight3) + self.biases3 # 网络输出 # 定义损失函数 with tf.name_scope('create_loss'): # 计算交叉熵损失函数 # 问题只有一个正确答案的时候,可以使用sparse_softmax_cross_entropy_with_logits来计算交叉熵 # 这个函数可以加速交叉熵的计算 # 但是这个函数需要提供的正确答案是一个数字,而不是one-hot向量 self.cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.y, labels=tf.argmax(self.y_, 1)) self.cross_entropy_mean = tf.reduce_mean(self.cross_entropy) tf.add_to_collection(name='loss', value=self.cross_entropy_mean) self.loss = tf.add_n(tf.get_collection('loss')) # 总的loss with tf.name_scope("optimizer"): self.optimizer = tf.train.AdamOptimizer(self.learning_rate_base).minimize(self.loss) with tf.name_scope("evaluate"): # 验证集 self.correct_prediction = tf.equal(tf.argmax(self.y, 1), tf.argmax(self.y_, 1)) self.accuracy = tf.reduce_mean(tf.cast(self.correct_prediction, dtype=tf.float32), name="accuracy") # tensorboard显示数据 with tf.name_scope("summary"): tf.summary.scalar("loss", self.loss) tf.summary.scalar("accuracy", self.accuracy) self.summary_op = tf.summary.merge_all() def train(self, train_data, train_step=300): """ 训练数据 :param train_data: :param train_step: :return: """ with tf.Session() as sess: init_op = tf.group(tf.local_variables_initializer(), tf.global_variables_initializer()) sess.run(init_op) # train_data = TrainData(data_path=train_data_path, batch_size=self.batch_size) board_data = r"E:\back_up\code\112\tensorflow_project\newbook\chapter3\model\tensorboard" # tensorboard数据保存路径 model_save = r'E:\back_up\code\112\tensorflow_project\newbook\chapter3\model\garph\graph' writer = tf.summary.FileWriter(logdir=board_data, graph=sess.graph) # 保存tensorboard中的数据 saver = tf.train.Saver(tf.global_variables(), max_to_keep=2) # 保存模型 for step in range(train_step): # 开始训练 xs, ys = train_data.next_batch() feed_dict = {self.x: xs, self.y_: ys} _, loss, summary = sess.run([self.optimizer, self.loss, self.summary_op], feed_dict=feed_dict) # print("loss after {} steps is {}".format(step, loss)) if step % 200 == 0: feed_dict = {self.x: train_data.validation_data()[0], self.y_: train_data.validation_data()[1]} accuracy = sess.run(self.accuracy, feed_dict=feed_dict) print("After {} step accuracy is {}".format(step, accuracy)) # 保存模型 saver.save(sess=sess, save_path=model_save) # 保存tensorboard数据 writer.add_summary(summary=summary, global_step=step) def predict(train_data): """ 对输入的值进行预测 :param train_data: 输入数据 :return: """ # 加载训练好的模型 model_load = r"E:\back_up\code\112\tensorflow_project\newbook\chapter3\model\garph" sess = tf.Session() check_point_file = tf.train.latest_checkpoint(model_load) saver = tf.train.import_meta_graph("{}.meta".format(check_point_file), clear_devices=True) saver.restore(sess=sess, save_path=check_point_file) graph = sess.graph test_data = graph.get_operation_by_name("placeholder/x-input").outputs[0] test_label = graph.get_operation_by_name("placeholder/y-input").outputs[0] test_accuracy = graph.get_operation_by_name("evaluate/accuracy").outputs[0] feed_dict = {test_data: train_data.test_data()[0], test_label: train_data.test_data()[1]} accuracy = sess.run(test_accuracy, feed_dict=feed_dict) print("The test accuracy is {}".format(accuracy)) if __name__ == '__main__': data_path = r'E:\code\112\tensorflow_project\chapter5\data\tensorflow_data' train_data = TrainData(data_path=data_path, batch_size=200) model = Model(input_node=784, output_node=10, layer1_node=500, layer2_node=400, regularizer=True) model.train(train_data=train_data, train_step=30000) print("---------test-----------") predict(train_data=train_data)
运行程序开始训练:
在cmd中输入如下命令:
tensorboard -logdir="E:\back_up\code\112\tensorflow_project\newbook\chapter3\model\garph"
如下图所示:
将后面的地址复制到浏览器:
可以看到训练的loss和验证集的accuracy:
最后,通过predict()函数加载训练好的模型,求出测试集在模型上的精度:
神经网络模型最终的目标是对未知数据提供判断,所以为了对模型未知数据上的表现进行判断,需要保证测试数据在训练过程中不可见,为了评估模型在不同参数下的效果,通常会从训练数据中抽取一部分数据作为验证集。如果验证数据集不能很好的代表测试数据集的分布,则模型在这两个数据集上的表现很可能是不一样的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)