TensorFlow基础:从零构建与训练简单神经网络

夏知夏
夏知夏
发布时间:2026-10-14| 5人浏览过
原创

本文详细讲解使用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防止过拟合。

附上训练集数据

以上内容为本文全部总结,希望对各位的学习有所帮助,也欢迎大家继续支持本站。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。