TensorFlow中tf.while_loop循环变量作用域与修改机制详解

Luna
Luna
发布时间:2026-10-12| 5人浏览过
原创

本文深入解析TensorFlow中tf.while_loop的执行机制,重点探讨全局变量和类成员变量在循环体内的行为。通过代码案例说明,非loop_vars指定的变量不会参与图循环,且print等操作在定义时即执行。文章指出,只有传入loop_vars并返回的变量才会真正随循环迭代更新,其他变量仅能只读或重置,帮助开发者避免常见的状态管理错误。

直接来看代码示例。

import tensorflow as tf

n1 = tf.constant(2)
n2 = tf.constant(3)
n3 = tf.constant(4)

def cond1(i, a, b):
 return i < n1

def cond2(i, a, b):
 return i < n2

def cond3(i, a, b):
 return i < n3

def body(i, a, b):
 return i + 1, b, a + b

i1, a1, b1 = tf.while_loop(cond1, body, (2, 1, 1))
i2, a2, b2 = tf.while_loop(cond2, body, (2, 1, 1))
i3, a3, b3 = tf.while_loop(cond3, body, (2, 1, 1))
sess = tf.Session()

print(sess.run(i1))
print(sess.run(a1))
print(sess.run(b1))
print("-")
print(sess.run(i2))
print(sess.run(a2))
print(sess.run(b2))
print("-")
print(sess.run(i3))
print(sess.run(a3))
print(sess.run(b3))

运行结果如下:

2
1
1
-
3
1
2
-
4
2
3

可以看出,body函数返回的三个变量被重新传回了body函数。

补充知识:如何在TensorFlow的tf.while_loop循环(非普通Python循环)中操作变量?

以下是操作全局变量的代码示例。

xiaojie=1
i=tf.constant(0,dtype=tf.int32)
batch_len=tf.constant(10,dtype=tf.int32)
loop_cond = lambda a,b: tf.less(a,batch_len)
#yy=tf.Print(batch_len,[batch_len],"batch_len:")
yy=tf.constant(0)
loop_vars=[i,yy]
def _recurrence(i,yy):
 c=tf.constant(2,dtype=tf.int32)
 x=tf.multiply(i,c)
 global xiaojie
 xiaojie=xiaojie+1
 print_info=tf.Print(x,[x],"x:")
 yy=yy+print_info
 i=tf.add(i,1)
# print (xiaojie)
 return i,yy
i,yy=tf.while_loop(loop_cond,_recurrence,loop_vars,parallel_iterations=1)#可以批处理
sess = tf.Session()
print (sess.run(i))
print (xiaojie)

输出结果为10和2。

这表明变量xiaojie仅被修改了一次。

此时,在_recurrence循环体内部添加如下语句:

print(xiaojie)

这将输出2,且仅输出一次。具体原因将在最后的总结部分进行解释。

以下是操作类成员变量的代码示例,定义了一个RNN_Model类:

def __init__(self):
  self.xiaojie=1
 def test_RNN(self):
  i=tf.constant(0,dtype=tf.int32)
  batch_len=tf.constant(10,dtype=tf.int32)
  loop_cond = lambda a,b: tf.less(a,batch_len)
  #yy=tf.Print(batch_len,[batch_len],"batch_len:")
  yy=tf.constant(0)
  loop_vars=[i,yy]
  def _recurrence(i,yy):
   c=tf.constant(2,dtype=tf.int32)
   x=tf.multiply(i,c)
   self.xiaojie=self.xiaojie+1
   print_info=tf.Print(x,[x],"x:")
   yy=yy+print_info
   i=tf.add(i,1)        print ("_recurrence:",self.xiaojie)
   return i,yy
  i,yy=tf.while_loop(loop_cond,_recurrence,loop_vars,parallel_iterations=1)#可以批处理
  sess = tf.Session()
  sess.run(yy)
  print (self.xiaojie)
if __name__ == "__main__":
 model = RNN_Model()#构建树,并且构建词典
 model.test_RNN()

输出结果如下:

_recurrence: 2
10
2

总结tf.while_loop对全局变量和类成员变量的操作机制

为什么_recurrence中定义的print操作只执行一次?这是因为_recurrence中的print语句相当于对代码的定义,它在定义过程中就直接执行了。因此,可以看到输出发生在sess.run之前。然而,定义的其他操作属于数据流图中的操作,必须在sess.run中执行。

必须在sess.run中执行。无论是全局变量xiaojie还是类成员变量xiaojie,它们都不属于计算图的内容。因此,tf.while_loop执行的是TensorFlow计算图中的循环,不在计算图中的变量不会参与循环。注意:必须是与loop_vars中指定的变量存在数据依赖关系的tensor才可以!此外,即使存在依赖关系,也必须是_recurrence循环体中return出的变量,才会真正发生变化。例如,见下面的self.L。总之,想要操纵变量,必须将其传入loop_vars!

如果对一个变量没有修改需求,可以直接在循环中以操纵类成员变量或全局变量的方式进行只读访问。

self.L与loop_vars中的变量存在依赖关系,但并未被真正修改。

#IIII通过计算将非叶子节点的词向量也放入nodes_tensor中。
   iiii=tf.constant(0,dtype=tf.int32)
   loop____cond = lambda a,b,c,d,e: tf.less(a,self.sentence_length-1)#iiii的范围是0到sl-2。注意,不包括sl-1。这是因为只需要计算sentence_length-1次,就能构建出一颗树
   loop____vars=[iiii,columnLinesOfL,node_tensors_cost_tensor,nodes_tensor,tfPrint]
   def ____recurrence(iiii,columnLinesOfL,node_tensors_cost_tensor,nodes_tensor,tfPrint):#循环的目的是实现Greedy算法
    ###
    #Greedy的主要目标就是确立树结构。    
    ###  
    c1 = self.L[:,0:columnLinesOfL-1]#这段代码是从RvNN的matlab的源码中复制过来的,但是Matlab的下标是从1开始,并且Matlab中1:2就是1和2,而python中1:2表示的是1,不包括2,所以,有很大的不同。
    c2 = self.L[:,1:columnLinesOfL]
    c=tf.concat([c1,c2],axis=0)
    p=tf.tanh(tf.matmul(self.W1,c)+tf.tile(self.b1,[1,columnLinesOfL-1]))
    p_normalization=self.normalization(p)
    y=tf.tanh(tf.matmul(self.U,p_normalization)+tf.tile(self.bs,[1,columnLinesOfL-1]))#根据Matlab中的源码来的,即重构后,也有一个激活的过程。
    #将Y矩阵拆分成上下部分之后,再分别进行标准化。
    columnlines_y=columnLinesOfL-1
    (y1,y2)=self.split_by_row(y,columnlines_y)
    y1_normalization=self.normalization(y1)
    y2_normalization=self.normalization(y2)
    #论文中提出一种计算重构误差时要考虑的权重信息。具体见论文,这里暂时不实现。
    #这个权重是可以修改的。
    alpha_cat=1 
    bcat=1
    #计算重构误差矩阵
##    constant1=tf.constant([[1.0,2.0,3.0],[4.0,5.0,6.0],[7.0,8.0,9.0]])
##    constant2=tf.constant([[1.0,2.0,3.0],[1.0,4.0,2.0],[1.0,6.0,1.0]])
##    constructionErrorMatrix=self.constructionError(constant1,constant2,alpha_cat,bcat)
    y1c1=tf.subtract(y1_normalization,c1)
    y2c2=tf.subtract(y2_normalization,c2)    
    constructionErrorMatrix=self.constructionError(y1c1,y2c2,alpha_cat,bcat)
################################################################################
    print_info=tf.Print(iiii,[iiii],"\niiii:")#专门为了调试用,输出相关信息。
    tfPrint=print_info+tfPrint
    print_info=tf.Print(columnLinesOfL,[columnLinesOfL],"\nbefore modify. columnLinesOfL:")#专门为了调试用,输出相关信息。
    tfPrint=print_info+tfPrint
    print_info=tf.Print(constructionErrorMatrix,[constructionErrorMatrix],"\nbefore modify. constructionErrorMatrix:",summarize=100)#专门为了调试用,输出相关信息。
    tfPrint=tf.to_int32(print_info[0])+tfPrint#一种不断输出tf.Print的方式,注意tf.Print的返回值。
################################################################################
    J_minpos=tf.to_int32(tf.argmin(constructionErrorMatrix))#如果不转换的话,下面调用delete_one_column中,会调用tf.slice,之后tf.slice的参数中的类型必须是一样的。
    J_min=constructionErrorMatrix[J_minpos]
    #一共要进行sl-1次循环。因为是从sl个叶子节点,两两结合sl-1次,才能形成一颗完整的树,而且是采用Greedy的方式。
    #所以,需要为下次循环做准备。
    #第一步,从该sentence的词向量矩阵中删除第J_minpos+1列,因为第J_minpos和第J_minpos+1列对应的单词要合并为一个新的节点,这里就是修改L
################################################################################
    print_info=tf.Print(self.L,[self.L[0]],"\nbefore modify. L row 0:",summarize=100)#专门为了调试用,输出相关信息。
    tfPrint=tf.to_int32(print_info[0][0])+tfPrint
    print_info=tf.Print(self.L,[tf.shape(self.L)],"\nbefore modify. L shape:")#专门为了调试用,输出相关信息。
    tfPrint=tf.to_int32(print_info[0][0])+tfPrint
################################################################################
    deleteColumnIndex=J_minpos+1
    self.L=self.delete_one_column(self.L,deleteColumnIndex,self.numlinesOfL,columnLinesOfL)
    columnLinesOfL=tf.subtract(columnLinesOfL,1) #列数减去1.
################################################################################
    print_info=tf.Print(deleteColumnIndex,[deleteColumnIndex],"\nbefore modify. deleteColumnIndex:")#专门为了调试用,输出相关信息。
    tfPrint=print_info+tfPrint
    print_info=tf.Print(self.L,[self.L[0]],"\nafter modify. L row 0:",summarize=100)#专门为了调试用,输出相关信息。
    tfPrint=tf.to_int32(print_info[0][0])+tfPrint
    
    print_info=tf.Print(self.L,[tf.shape(self.L)],"\nafter modify. L shape:")#专门为了调试用,输出相关信息。
    tfPrint=tf.to_int32(print_info[0][0])+tfPrint
    print_info=tf.Print(columnLinesOfL,[columnLinesOfL],"\nafter modify. columnLinesOfL:")#专门为了调试用,输出相关信息。
    tfPrint=print_info+tfPrint
################################################################################
    
    #第二步,将新的词向量赋值给第J_minpos列
    columnTensor=p_normalization[:,J_minpos]
    new_column_tensor=tf.expand_dims(columnTensor,1)
    self.L=self.modify_one_column(self.L,new_column_tensor,J_minpos,self.numlinesOfL,columnLinesOfL)
    #第三步,同时将新的非叶子节点的词向量存入nodes_tensor
    modified_index_tensor=tf.to_int32(tf.add(iiii,self.sentence_length))
    nodes_tensor=self.modify_one_column(nodes_tensor,new_column_tensor,modified_index_tensor,self.numlines_tensor,self.numcolunms_tensor)
    #第四步:记录合并节点的最小损失,存入node_tensors_cost_tensor
    J_min_tensor=tf.expand_dims(tf.expand_dims(J_min,0),1)
    node_tensors_cost_tensor=self.modify_one_column(node_tensors_cost_tensor,J_min_tensor,iiii,self.numlines_tensor2,self.numcolunms_tensor2)
    ####进入下一次循环
    iiii=tf.add(iiii,1)
    print_info=tf.Print(J_minpos,[J_minpos,J_minpos+1],"node:")#专门为了调试用,输出相关信息。
    tfPrint=tfPrint+print_info
#    columnLinesOfL=tf.subtract(columnLinesOfL,1) #在上面的循环体中已经执行了,没有必要再执行。
    return iiii,columnLinesOfL,node_tensors_cost_tensor,nodes_tensor,tfPrint
   iiii,columnLinesOfL,node_tensors_cost_tensor,nodes_tensor,tfPrint=tf.while_loop(loop____cond,____recurrence,loop____vars,parallel_iterations=1)
   pass

上述代码实现了Greedy算法,用于递归构建神经网络树结构。

但程序曾出现错误,经过不断调试后发现,self.L虽然与循环loop____vars中的变量存在依赖关系,即在tf.while_loop进行循环时可以输出其值。

然而,它每次都无法真正意义上修改self.L。会发现,每次循环结束后,进入下一次循环时,self.L仍然没有变化。

执行结果如下:

before modify. columnLinesOfL:[31]
iiii:[0]

after modify. columnLinesOfL:[30]

before modify. L shape:[300 31]

before modify. L row 0:[0.126693 -0.013654 -0.166731 -0.13703 -0.261395 0.11459 0.016001 0.016001 0.144603 0.05588 0.171787 0.016001 1.064545 0.144603 0.130615 -0.13703 -0.261395 1.064545 -0.261395 0.144603 0.036626 1.064545 0.188871 0.201198 0.05588 0.203795 0.201198 0.03536 0.089345 0.083778 0.103635]
node:[0][1]

before modify. constructionErrorMatrix:[3.0431733686706206 11.391056715427794 19.652819956115856 13.713453313903868 11.625973829805879 12.827533320819564 9.7513513723204746 13.009151292890811 13.896089243289065 10.649829109971648 9.45239374745086 15.704486086921641 18.274065790781862 12.447866299915024 15.302996103637689 13.713453313903868 14.295549844738751 13.779406175789358 11.625212314259059 16.340507223201449 19.095964364689717 15.10149194936319 11.989443162329437 13.436654650354058 11.120373311110505 12.39345317975002 13.568052800712424 10.998430341124633 8.3223909323599869 6.8896857405641851]

after modify. L shape:[300 30]

after modify. L row 0:[0.126693 -0.166731 -0.13703 -0.261395 0.11459 0.016001 0.016001 0.144603 0.05588 0.171787 0.016001 1.064545 0.144603 0.130615 -0.13703 -0.261395 1.064545 -0.261395 0.144603 0.036626 1.064545 0.188871 0.201198 0.05588 0.203795 0.201198 0.03536 0.089345 0.083778 0.103635]

before modify. deleteColumnIndex:[1]

before modify. columnLinesOfL:[30]

iiii:[1]

before modify. L shape:[300 31]

after modify. columnLinesOfL:[29]

before modify. L row 0:[0.126693 -0.013654 -0.166731 -0.13703 -0.261395 0.11459 0.016001 0.016001 0.144603 0.05588 0.171787 0.016001 1.064545 0.144603 0.130615 -0.13703 -0.261395 1.064545 -0.261395 0.144603 0.036626 1.064545 0.188871 0.201198 0.05588 0.203795 0.201198 0.03536 0.089345 0.083778 0.103635]

before modify. deleteColumnIndex:[1]
node:[0][1]

before modify. constructionErrorMatrix:[3.0431733686706206 11.391056715427794 19.652819956115856 13.713453313903868 11.625973829805879 12.827533320819564 9.7513513723204746 13.009151292890811 13.896089243289065 10.649829109971648 9.45239374745086 15.704486086921641 18.274065790781862 12.447866299915024 15.302996103637689 13.713453313903868 14.295549844738751 13.779406175789358 11.625212314259059 16.340507223201449 19.095964364689717 15.10149194936319 11.989443162329437 13.436654650354058 11.120373311110505 12.39345317975002 13.568052800712424 10.998430341124633 8.3223909323599869]

after modify. L shape:[300 29]

after modify. L row 0:[0.126693 -0.166731 -0.13703 -0.261395 0.11459 0.016001 0.016001 0.144603 0.05588 0.171787 0.016001 1.064545 0.144603 0.130615 -0.13703 -0.261395 1.064545 -0.261395 0.144603 0.036626 1.064545 0.188871 0.201198 0.05588 0.203795 0.201198 0.03536 0.089345 0.083778]

before modify. columnLinesOfL:[29]

iiii:[2]

后面那个after modify时L shape为[300 29]的原因是:执行

self.L=self.modify_one_column(self.L,new_column_tensor,J_minpos,self.numlinesOfL,columnLinesOfL)

时,columnLinesOfL是循环loop____vars中的变量,因此会随着每次循环发生变化。我写的modify_one_column见我的博文“修改tensor张量矩阵的某一列”。它决定了

修改后tensor的维度。

但是,无论如何,每一次循环,都是

before modify. L shape:[300 31]

说明self.L在循环体中虽然被修改了,但是下次循环又会被重置为初始值。

以上这篇基于tensorflow for循环 while循环案例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

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

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 算法库。