TensorFlow基础:从零构建与训练简单神经网络
本文详细讲解使用TensorFlow构建简单神经网络的完整流程。从特征提取、网络结构定义(输入层、隐藏层、输出层),到损失函数(交叉熵)与优化算法(梯度下降)的选择,再到动态学习率、正则化及Dropout等提升模型准确率的关键策略。通过线性与非线性模型的对比,深入解析代码实现与参数调整技巧,帮助初学者掌握模型训练核心逻辑。
TensorFlow神经网络构建与训练指南
近期开始接触TensorFlow框架,特此整理一份学习笔记以供回顾。
神经网络解决问题的标准流程
一、神经网络解决问题的标准流程:
第一步,提取问题中实体的特征向量作为输入。这涉及对数据集进行特征工程处理,明确每个样本的特征维度,从而确定输入层神经元的数量。
第二步,定义神经网络的具体结构,并明确从输入到输出的映射关系。这包括确定输入层、隐藏层以及输出层的配置。
第三步,利用训练数据调整网络参数,即模型训练过程。此阶段需定义损失函数及参数优化方法,例如采用交叉熵损失函数配合梯度下降法进行调优。
第四步,利用训练完成的模型对未知数据进行预测,以此评估模型的性能与优劣。
简单向前传播神经网络的实现
二、训练简单的向前传播神经网络
此处演示的模型属于最基础的线性神经网络类型,未使用激活函数进行非线性化处理,且不包含反向传播机制,旨在直观展示神经网络的基本工作流程。
import tensorflow as tf
#定义隐藏层参数,每个w变量是一个tensor(可以当成是n*m的数组,n表示上一层结点个数,m表示本层结点个数)表示上一层与本层的连接权重,这里先随机定义权重
w1=tf.Variable(tf.random_normal([2,3],stddev=1))
w2=tf.Variable(tf.random_normal([3,1],stddev=1))
#定义存放输入数据的地方,也就是x向量,这里shape为前一个传入训练的样本个数,后面出入每个样本的维度大小
x=tf.placeholder(tf.float32,shape=(None,2),name="input")
#矩阵乘法
a=tf.matmul(x,w1)
y=tf.matmul(a,w2)
with tf.Session() as sess:
#新版本好像不能用这个函数初始化所有变量了
init_op=tf.initialize_all_variables()
sess.run(init_op)
#feed_dict用于向y中的x传入参数,这里传入3个,则y输出为一个3*1的tensor
print(sess.run(y,feed_dict={x:[[0.7,0.9],[1.0,1.5],[2.1,2.3]]}))
至此,一个线性神经网络定义完毕:其通过x的每个维度分别乘以两层权重,最终输出单个数值。
损失函数、反向传播与优化策略
三、定义损失函数以及反向传播算法
基于上述基础,我们可以定义损失函数及反向传播算法来拟合数据。对于非线性数据,可引入激活函数进行线性化处理。此外需注意学习率的选择:本文采用动态学习率策略,初始设定较大值以加速收敛,随后随迭代次数增加而递减,以避免错过局部最小值。关于防止过拟合,常见策略包括正则化和Dropout,本文暂不深入讨论后者。
损失函数:交叉熵
反向传播算法:梯度下降法
激活函数:ReLU
# -*- coding: utf-8 -*-
"""
Created on Fri Aug 18 14:02:19 2017
@author: osT
"""
import tensorflow as tf
import numpy as np
#导入数据,这里的数据是每一行代表一个样本,每一行最后一列表示样本标签,0-32一共33个类
data=np.loadtxt('train_data.txt',dtype='float',delimiter=',')
#将样本标签转换成独热编码
def label_change(before_label):
label_num=len(before_label)
change_arr=np.zeros((label_num,33))
for i in range(label_num):
#该样本标签原本为0-32的,本人疏忽下32标记成33
if before_label[i]==33.0:
change_arr[i,int(before_label[i]-1)]=1
else:
change_arr[i,int(before_label[i])]=1
return change_arr
#定义神经网络的输入输出结点,每个样本为1*315维,以及输出分类结果
INPUT_NODE=315
OUTPUT_NODE=33
#定义两层隐含层的神经网络,一层300个结点,一层100个结点
LAYER1_NODE=300
LAYER2_NODE=100
#定义学习率,学习率衰减速度,正则系数,训练调整参数的次数以及平滑衰减率
LEARNING_RATE_BASE=0.5
LEARNING_RATE_DECAY=0.99
REGULARIZATION_RATE=0.0001
TRAINING_STEPS=2000
MOVING_AVERAGE_DECAY=0.99
#定义整个神经网络的结构,也就是向前传播的过程,avg_class为平滑可训练量的类,不传入则不使用平滑
def inference(input_tensor,avg_class,w1,b1,w2,b2,w3,b3):
if avg_class==None:
#第一层隐含层,输入与权重矩阵乘后加上常数传入激活函数作为输出
layer1=tf.nn.relu(tf.matmul(input_tensor,w1)+b1)
#第二层隐含层,前一层的输出与权重矩阵乘后加上常数作为输出
layer2=tf.nn.relu(tf.matmul(layer1,w2)+b2)
#返回 第二层隐含层与权重矩阵乘加上常数作为输出
return tf.matmul(layer2,w3)+b3
else:
#avg_class.average()平滑训练变量,也就是每一层与上一层的权重
layer1=tf.nn.relu(tf.matmul(input_tensor,avg_class.average(w1))+avg_class.average(b1))
layer2=tf.nn.relu(tf.matmul(layer1,avg_class.average(w2))+avg_class.average(b2))
return tf.matmul(layer2,avg_class.average(w3))+avg_class.average(b3)
def train(data):
#混洗数据
np.random.shuffle(data)
#取钱850个样本为训练样本,后面的全是测试样本,约250个
data_train_x=data[:850,:315]
data_train_y=label_change(data[:850,-1])
data_test_x=data[850:,:315]
data_test_y=label_change(data[850:,-1])
#定义输出数据的地方,None表示无规定一次输入多少训练样本,y_是样本标签存放的地方
x=tf.placeholder(tf.float32,shape=[None,INPUT_NODE],name='x-input')
y_=tf.placeholder(tf.float32,shape=[None,OUTPUT_NODE],name='y-input')
#依次定义每一层与上一层的权重,这里用随机数初始化,注意shape的对应关系
w1=tf.Variable(tf.truncated_normal(shape=[INPUT_NODE,LAYER1_NODE],stddev=0.1))
b1=tf.Variable(tf.constant(0.1,shape=[LAYER1_NODE]))
w2=tf.Variable(tf.truncated_normal(shape=[LAYER1_NODE,LAYER2_NODE],stddev=0.1))
b2=tf.Variable(tf.constant(0.1,shape=[LAYER2_NODE]))
w3=tf.Variable(tf.truncated_normal(shape=[LAYER2_NODE,OUTPUT_NODE],stddev=0.1))
b3=tf.Variable(tf.constant(0.1,shape=[OUTPUT_NODE]))
#输出向前传播的结果
y=inference(x,None,w1,b1,w2,b2,w3,b3)
#每训练完一次就会增加的变量
global_step=tf.Variable(0,trainable=False)
#定义平滑变量的类,输入为平滑衰减率和global_stop使得每训练完一次就会使用平滑过程
variable_averages=tf.train.ExponentialMovingAverage(MOVING_AVERAGE_DECAY,global_step)
#将平滑应用到所有可训练的变量,即trainable=True的变量
variable_averages_op=variable_averages.apply(tf.trainable_variables())
#输出平滑后的预测值
average_y=inference(x,variable_averages,w1,b1,w2,b2,w3,b3)
#定义交叉熵和损失函数,但为什么传入的是label的arg_max(),就是对应分类的下标呢,我们迟点再说
cross_entropy=tf.nn.sparse_softmax_cross_entropy_with_logits(logits=y,labels=tf.arg_max(y_,1))
#计算交叉熵的平均值,也就是本轮训练对所有训练样本的平均值
cross_entrip_mean=tf.reduce_mean(cross_entropy)
#定义正则化权重,并将其加上交叉熵作为损失函数
regularizer=tf.contrib.layers.l2_regularizer(REGULARIZATION_RATE)
regularization=regularizer(w1)+regularizer(w2)+regularizer(w3)
loss=cross_entrip_mean+regularization
#定义动态学习率,随着训练的步骤增加不断递减
learning_rate=tf.train.exponential_decay(LEARNING_RATE_BASE,global_step,900,LEARNING_RATE_DECAY)
#定义向后传播的算法,梯度下降发,注意后面的minimize要传入global_step
train_step=tf.train.GradientDescentOptimizer(learning_rate).minimize(loss,global_step=global_step)
#管理需要更新的变量,传入的参数是包含需要训练的变量的过程
train_op=tf.group(train_step,variable_averages_op)
#正确率预测
correct_prediction=tf.equal(tf.arg_max(average_y,1),tf.arg_max(y_,1))
accuracy=tf.reduce_mean(tf.cast(correct_prediction,tf.float32))
with tf.Session() as sess:
#初始所有变量
tf.global_variables_initializer().run()
#训练集输入字典
validate_feed={x:data_train_x,y_:data_train_y}
#测试集输入字典
test_feed={x:data_test_x,y_:data_test_y}
for i in range(TRAINING_STEPS):
if i%1000==0:
validate_acc=sess.run(accuracy,feed_dict=validate_feed)
print("After %d training step(s),validation accuracy using average model is %g"%(i,validate_acc))
#每一轮通过同一训练集训练,由于样本太少,没办法了
sess.run(train_op,feed_dict=validate_feed)
#用测试集查看模型的准确率
test_acc=sess.run(accuracy,feed_dict=test_feed)
print("After %d training step(s),test accuracy using average model is %g"%(TRAINING_STEPS,test_acc))
train(data)
接下来分析为何在计算交叉熵时需传入样本下标:
已知输出节点共有33个,经权重相乘后,每个节点产生一个输出值。我们将该值视为样本属于对应类别的概率,值越大表示样本越可能属于该类。logits参数为神经网络未经Softmax处理的直接输出;labels传入的是单个整数值,即分类对应的下标。这源于所使用的函数tf.nn.sparse_softmax_cross_entropy_with_logits(),该函数针对仅有一个正确分类的模型计算进行了加速优化。labels输入即为“这一个正确的分类”,对应输出节点的下标。若使用未加速的交叉熵函数tf.nn.softmax_cross_entropy_with_logits(),则需传入完整的标签向量。
最后,总结提升模型准确率的关键方法:
1. 使用激活函数:实现非线性化,这几乎是必要步骤。\n\n2. 增加隐含层:以本例为例,单隐含层300个节点时准确率约89%;单隐含层400个节点时准确率约93%;双隐含层(300节点与100节点)时准确率约94%。但增加隐含层会延长训练时间。\n\n3. 使用动态学习率:不仅加速训练,还能增加神经网络收敛至较低极小值的概率,从而提升准确率。\n\n4. 使用平滑模型:主要增强模型健壮性,提升泛化能力。\n\n5. 加入正则化或使用Dropout防止过拟合。
附上训练集数据
以上内容为本文全部总结,希望对各位的学习有所帮助,也欢迎大家继续支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









