为什么tensorflow要有Variable的对象?
编程语言中,都有变量之概念,用于保存中间计算结果,如计算100以内正整数的累加和.
int sum = 0;
for (int i = 0 ;i<=100;i++){
sum += i;
}
上述c++代码中,sum定义变量用于保存累加和.在内部实现时,会为sum变量分配一块固定内存,每次循环,该内存值会变化,但内存地址不变.sum变量和地址是绑定的.
以下为python语言的实现
sum = 0
for i in range(0,101):
sum += i
print(id(sum))
也定义了变量sum,观察输出,会发现输出id(sum)在变化,即变量sum的地址在变化.在python中,变量不是和内存的地址绑定的.变量只是对象的一个名称.赋值语句将不同对象赋给某个变量,会改变变量的地址,其实相当于一个新的变量,只是名称没变.
tensorflow中有很多求变量的梯度操作,即变量变化对函数值的影响.该操作要根据变量变化前后的值来计算,如果变量的地址变化,则会增加实现难度.tensorflow通过定义Variable对象来简化梯度的计算.
with tf.GradientTape() as tape: # with结构记录梯度信息
y = tf.matmul(x_train, w1) + b1 # 神经网络乘加运算
y = tf.nn.softmax(y) # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)
y_ = tf.one_hot(y_train, depth=3) # 将标签值转换为独热码格式,方便计算loss和accuracy
loss = tf.reduce_mean(tf.square(y_ - y)) # 采用均方误差损失函数mse = mean(sum(y-out)^2)
loss_all += loss.numpy() # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
# 计算loss对各个参数的梯度
grads = tape.gradient(loss, [w1, b1])
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
w1.assign_sub(lr * grads[0]) # 参数w1自更新
b1.assign_sub(lr * grads[1]) # 参数b自更新
上述代码用梯度下降算法训练网络参数.GradientTape对象用于跟踪参数的变化对损失函数值的影响(梯度),网络参数定义为Variable对象,GradientTape会记录Variable的变化,从而计算出梯度.如果用普通变量来定义网络参数,那么每次迭代更新,网络参数就是一个新的对象.GradientTape无法跟踪这些新变量.因此,将网络参数定义成Variable,就是为了让GradientTape跟踪变化,从而计算梯度.在更新网络参数时,用Variable.assign_xxx方法,而不能用赋值操作符,赋值操作符(=)会返回一个新的Variable.
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
#w1.assign_sub(lr * grads[0]) # 参数w1自更新
#b1.assign_sub(lr * grads[1]) # 参数b自更新
w1 = w1 - lr*grads[0]#w1就是一个新的对象!!!!
b1 = b1 - lr*grads[1] #b1就是一个新的对象!!!!
上述代码会在第二次迭代时出错.