新手初识Tensorflow

成功解决tensorflow.python.framework.errors_impl.FailedPreconditionError: logs is not a directory报错 在进行深度学习时,程序报错tensorflow.python.framework.errors_impl.FailedPreconditionError: XXX is not a directory。XXX是文件夹目录。在确保代码正确后,我认识到是装配环境有问题,并检查了如下环境:cuda、cudnn、pytorch、tensorflow、python,在重装完以上环境后,代码顺利跑通了。变量指向的目录存在,确保路径字符串是正确的,并且不包含任何非法字符。目录下不存在名为XXX 的同名文件,而非目录。 阅读详情

前些日子偶然接触到Tensorflow,作为一名新手,本人根据Tensorflow中文社区中的文档进行了基础部分的学习,并对学习过程以及学习过程中的问题一并进行了记录。

根据本机代码结果整理出这篇文档,希望对大家有所帮助。如果本文任何地方的表述或某些个人理解有所不妥,欢迎各位大神批评指正。

注:若本机代码实验运行结果与官方文档不符,本文中的结果记录为本机运行结果。

另注:本文有点小长且有些枯燥……

本文参考:Tensorflow中文社区

 

【零】Tensorflow介绍:

TensorFlow是一个非常强大的,用来做大规模数值计算的库,其所擅长的任务之一就是实现以及训练深度神经网络。

采用数据流图(data flow graphs),用于数值计算;
节点(nodes)表示数学操作,被称为op(operation);
线(edges)表示在节点间相互联系的n维数据数组——张量(tensor);
一个op获得0个或多个tensor执行计算,产生0个或多个tensor。

Tensorflow使用图(graph)表示计算任务;
在会话(session)的上下文(context)中执行图,调用Session.run()方法执行操作(Session对象使用之后需要关闭以释放资源,个人理解类似C语言中的malloc或C++中的new);
使用张量(tensor)表示数据,一个tensor包含一个静态类型rank和一个shape;
通过变量(variable)维护图执行过程中的状态信息;
使用feed、fetch为任意操作赋值或获取数据。

图必须在会话里被启动,会话将op分发到GPU或CPU一类的设备上,同时提供执行op的方法,这些方法执行后,将产生的tensor返回。

Tensorflow程序通常被组织成一个构建阶段和一个执行阶段:
【构建阶段】op的执行步骤被描述成一个图(创建一个图表示和训练神经网络)
【执行阶段】使用会话执行图中的op(反复执行图中的训练op)

【一】会话(session)关闭的两种方式:
import tensorflow as tf
m1 = tf.constant([[3.,3.]])
m2 = tf.constant([[3.],[2.]])
p = tf.matmul(m1,m2)

1.显式调用close方法:
  sess = tf.Session()
  result = sess.run(p)
  print result
  sess.close()
2.利用with代码块执行图后自动关闭:
  with tf.Session() as sess:
    result = sess.run(p)
    print result

实验.
若:
m1 = tf.constant([[3.,3.]])
m2 = tf.constant([[3.],[2.]])
result = sess.run(p)
#则输出结果为:[[ 15.]]
result = sess.run([p])
#则输出结果为:[array([[ 15.]], dtype=float32)]

若:
m1 = tf.constant([[3,3]])
m2 = tf.constant([[3],[2]])
result = sess.run(p)
#则输出结果为:[[15]]
result = sess.run([p])
#则输出结果为:[array([[15]], dtype=int32)]

总结.
矩阵中元素后加小数点则张量类型为float32,不加小数点则张量类型为int32(类似C语言或python中的类型定义方式)。

【二】Session与InteractiveSession:
Tensorflow依赖于一个高效C++后端进行计算,利用session与后端进行连接。一般来讲,使用Tensorflow程序的流程是先创建图,再在session中启动。
Python交互环境中,可以使用InteractiveSession类代替Session类,利用它可以更加灵活地构建代码。
利用Tensor.eval()、Operation.run()方法代替Session.run()方法,避免使用变量持有会话(提高灵活性)。
InteractiveSession可以在运行图的时候插入由某些op构成的计算图(适合交互式环境),可以先构建session再定义操作;
Session要在启动Session前构建整个图,需要在构建会话前定义好全部操作。
eg:
import tensorflow as tf
sess = tf.InteractiveSession()

x = tf.Variable([1.0, 2.0])
a = tf.constant([3.0, 3.0])

x.initializer.run()    #使用初始化器initializer的run()方法对x进行初始化(详见第四部分)

sub = tf.sub(x, a)
print sub.eval()

【三】张量的阶、形状和数据类型
1.阶:张量维数的数量描述
eg:
t = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]为二阶
零阶张量:纯量(s = 4)
一阶张量:向量(v = [1,2,3])
二阶张量:矩阵(m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]])    #可用语句t[i,j]访问
三阶张量:立体数据(t = [[[2], [4], [6]], [[8], [10], [12]], [[14], [16], [18]]])    #可用语句t[i,j,k]访问

2.形状:描述张量维度的三种记号:阶、形状、维度
阶   形状                         维数     
0     [ ]                             0-D     
1     [D0]                         1-D     
2     [D0, D1]                   2-D     
3     [D0, D1, D2]            3-D     
n     [D0, D1, ... Dn-1]     n-D     

3.数据类型:tensors的数据类型属性
数据类型               Python 类型            描述
DT_FLOAT              tf.float32               32 位浮点数.
DT_DOUBLE          tf.float64               64 位浮点数.
DT_INT64               tf.int64                   64 位有符号整型.
DT_INT32               tf.int32                   32 位有符号整型.
DT_INT16               tf.int16                   16 位有符号整型.
DT_INT8                 tf.int8                      8 位有符号整型.
DT_UINT8              tf.uint8                   8 位无符号整型.
DT_STRING            tf.string                  可变长度的字节数组.每一个张量元素都是一个字节数组.
DT_BOOL               tf.bool                    布尔型.
DT_COMPLEX64    tf.complex64        由两个32位浮点数组成的复数:实数和虚数.
DT_QINT32            tf.qint32                 用于量化Ops的32位有符号整型.
DT_QINT8              tf.qint8                    用于量化Ops的8位有符号整型.
DT_QUINT8           tf.quint8                 用于量化Ops的8位无符号整型.

【四】变量
通常会将一个统计模型中的参数表示为一组变量,变量必须进行初始化之后才有具体值。
例如, 将一个神经网络的权重作为某个变量存储在一个tensor中.在训练过程中, 通过重复运行训练图, 更新这个tensor。
eg:
state = tf.Variable(0, name = "counter")

one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)    #assign()将new_value赋值给state

init_op = tf.initialize_all_variables()    #启动图后, 变量必须先经过op初始化, 增加一个初始化op到图中

with tf.Session() as sess:
  sess.run(init_op)    #运行 'init' op
  print sess.run(state)
  for _ in range(3):
    sess.run(update)
    print sess.run(state)

输出:
0
1
2
3
 

Q.

该程序中,init_op=tf.initialize_all_variables() + sess.run(init_op)是否可以利用state.initialize.run()代替?

A.

可以,但要注意state.initialize.run()要在sess定义之后运行(本例中with语句后)。
否则会提示会话未创建信息:
ValueError: Cannot execute operation using `run()`: No default session is registered. Use `with sess.as_default():` or pass an explicit session to `run(session=sess)`
且对所有变量均需要进行初始化操作。

 

Q.

是否可以不对变量进行初始化操作?

A.

不可以。

以本例为例,不进行初始化操作将会进行如下报错:
tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized value counter
     [[Node: _retval_counter_0_0 = _Retval[T=DT_INT32, index=0, _device = "/job:localhost/replica:0/task:0/device:CPU:0"](counter)]]

 

Q.

state = tf.Variable(0, name = "counter")一句中name = "counter"的作用是什么?

A.

用name关键字定义的节点和张量名称,是模型本身的一部分,会和模型一起存储到checkpoint或SavedModel里面去(训练的结果),可以被其他语言所调用(C++,Java等)。


【五】fetch机制
为取回操作的输出内容,在使用Session对象的run()调用执行图时,可以传入tensor,让这些tensor取回结果(可以取回多个tensor)。
eg:
input1 = tf.constant(3.0)
input2 = tf.constant(2.0)
input3 = tf.constant(5.0)
intermed = tf.add(input2, input3)
mul = tf.mul(input1, intermed)

with tf.Session() as sess:
  result = sess.run([mul, intermed])
  print result

输出:
[21.0, 7.0](本机输出与Tensorflow中文社区官方文档中的输出不同)

实验.
而若代码改为:
intermed = tf.add([input2], [input3])
mul = tf.multiply([input1], [intermed])
则输出结果为:

 

[array([[ 21.]], dtype=float32), array([ 7.], dtype=float32)]

总结.
Session.run()方法参数为矩阵(维度为2)时,将返回各元素数据类型等信息。
(注:注意区分张量维度)

【六】feed机制
TensorFlow提供了feed机制,该机制可以临时替代图中的任意操作中的tensor,可以对图中任何操作提交补丁,直接插入一个tensor。
feed使用一个tensor值临时替换一个操作的输出结果:提供feed数据作为run()调用的参数。
feed只在调用它的方法内有效,方法结束,feed 就会消失。
最常见的用例是将某些特殊的操作指定为"feed"操作,标记的方法是使用tf.placeholder()为这些操作创建占位符。
eg:
input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)
output = tf.multiply(input1, input2)

with tf.Session() as sess:
  print sess.run([output], feed_dict = {input1:[7.], input2:[2.]})

输出:
[array([ 14.], dtype=float32)]

与第五部分相同:
若赋值字典改为feed_dict = {input1:7., input2:2.}
输出为:[14.0]

(注:Tensorflow中文社区官方文档中的一些API诸如tf.mul(),tf.sub()已经弃用,需要改为tf.multiply()或tf.subtract())

【七】模型建立
1.建立抽象模型(很重要)

 

 

利用所建立的模型进行训练。

2.定义损失函数和训练方法
定义损失函数值为loss;
训练方法一般为梯度下降法:optimizer = tf.train.GradientDescentOptimizer(rateOfStudy);

 

 

参数rateOfStudy为学习速率,一般无定值,根据经验进行确定,且很多算法(SSD、YOLO等)在训练过程中可以多该参数进行改动。

3.确定训练目标
训练目标一般为将损失函数降到最低(loss减小):train = optimizer.minimize(cross_entropy)。

【八】MNIST数据集
60000行的训练数据集 + 10000行的测试数据集
MINST数据单元由两部分组成:手写数字图片(mnist.train.image)、对应标签(mnist.train.labels)

图片:每张图片由28*28(784)个像素点构成,故mnist.train.image是一个形状为[60000,784]的张量,60000用作图片索引,784用所像素点索引。该张量(矩阵)中的每个元素表示某图片中某像素点的强度值p,0<p<1。

标签:介于0-9的数字,用来描述图片中的数字。利用 "one-hot vectors" 表示标签数据,一个one-hot向量只有一位数字为1,其余维度数字均为0,故数字n表示为在第n维数字为1的10维向量(例如,标签0被表示为([1,0,0,0,0,0,0,0,0,0,0]))。
即,mnist.train.labels是一个[60000, 10]的数字矩阵。

【九】Softmax回归
MNIST中每张图片均表示一个数字,希望得到某图片代表每个数字的概率。
即,模型推测一张包含9的图片代表数字9的概率是80%,而判断其为8的概率为5%,它代表其它数字的概率可能更小。

 

 

softmax模型可以用于给不同的对象分配概率。

softmax回归分为两步:
1.
对图片像素值进行加权求和,为得到一张图片属于某数字类的证据(evidence):

 

 

如果该像素有很强的证据说明该图片不属于此类,则相应权值为负;若该像素拥有有利的证据支持该图片属于此类,则权值为正。

2.
由于输入往往带入某些无关的干扰量,故加入额外偏置量(bias)。

对于给定的输入图片x,它代表的数字为i的证据可以表示为:
evidence(i) = ∑W(i,j) * x(j) + b(i)
其中,W表示权重,b(i)表示数字i类的偏置量,j表示给定图片x的像素索引用于像素求和.利用softmax函数将证据转化为概率y:
y = softmax(evidence)
更进一步,我们可以将上式写为:
y = softmax(W*x+b)

其中,softmax可以看作一个激励函数或链接函数,将定义的线性函数的输出转换为10个数字类的概率分布(我们想要的格式)。
因此,给定一张图片,它对于每个数字的吻合度可以被softmax函数转换为一个概率值,该函数可定义为:
softmax(x) = normalize(exp(x))
将输入当成幂指数求值,再正则化结果值.幂运算表示更大的证据对应更大的假设模型里面的乘数权重,更少的证据意味着在假设模型中拥有更小的乘数系数.假设模型中不允许权值W<=0,softmax将权重值正则化,使它们总和为1,以此构造有效的概率分布。

【十】实现回归模型
Tensorflow先用图描述一系列可交互的计算操作,然后全部一起在python之外运行。

用x表示所有输入的MNIST图像像素的二维数组,输入的图像个数在定义图时无法确定,希望在运行计算时进行输入,而每一张图展平成784维的向量。
用二位浮点数张量x表示这些图,将x设置为一个占位符placeholder(因为图像个数无法确定,所以创建占位符先进行“占位”)。
x = tf.placeholder("float", [None, 784])
其中,None表示此张量的第一个维度可以为任何长度(任意图像个数)。

W、b为模型参数,可以在训练过程中被修改,设置为变量Variable。
W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
由于W、b是需要学习的值,所以他们的初值可以随意设置(训练过程中更改)。
注:
W的维度是[784,10],因为要用784维的图片向量乘以它得到一个10维的证据值向量,每一位对应不同数字类;
b的形状是[10],可以直接把它加到输出上面。

定义模型:
y = tf.nn.softmax(tf.matmul(x,W) + b)

【十一】训练模型
定义指标表示模型的好坏,该指标被成为成本(cost)或损失(loss),然后最小化该loss(或cost)。
一种较为常见的成本函数是交叉熵(cross-entropy),定义如下:
Hy'(y)=-∑y(i)' * log(y(i))
其中,y是预测的概率分布,y'是实际分布(输入的one-hot vector)。

为了计算交叉熵,添加一个新的占位符输入正确值:
y_ = tf.placeholder("float", [None,10])
计算交叉熵:
cross_entropy = -tf.reduce_sum(y_*tf.log(y))

Tensorflow有一张描述上述各计算单元的图,它自动确定变量如何影响最小化的成本值(使用反向传播算法),然后Tensorflow会利用选择的优化算法不断修改变量,降低loss(cost)。

梯度下降算法:
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
梯度下降算法是一个简单的学习过程,Tensorflow将每个变量一点点向成本不断降低的方向移动:
上面一行中的梯度下降算法以0.01的学习速率最小化交叉熵。

在运行计算之前,需要添加一个操作初始化变量:
init = tf.initialize_all_variables()
在Session中启动模型并初始化变量:
sess = tf.Session()
sess.run(init)
然后开始训练模型(本例中模型循环训练1000次):
for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)    #随机抓取100个批处理数据点
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})    #利用抓取的数据点替换之前的占位符运行train_step

【十二】评估模型
1.找出预测正确的标签.
tf.argmax函数:可以给出某tensor对象在某一维上的数据最大值所在的索引值。
标签向量由0,1组成,最大值即为1,索引位置即为类别标签。
tf.argmax(y,1)返回的是模型对于任一输入y预测到的标签值,tf.argmax(y_,1)代表正确的标签,可以用tf.equal检测预测是否正确。

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
该行代码会给出一组布尔值,可以将其转换为浮点数然后取平均值以确定正确预测项的比例。

accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
[True, False, True, True] 会变成 [1,0,1,1] ,取平均值后得到 0.75。

最后,计算所学习到的模型在测试数据集上的正确率.
print sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})

【十三】计算图
为了在Python中进行高效数值计算,通常利用numpy一类的库将耗时操作在python外部进行计算(通过其他语言更为高效的代码来实现,各种各样丰富的库也是python最牛的地方之一)。
Python代码的目的是用来构建这个可以在外部运行的计算图,安排计算图的哪一部分应该被运行。

【十四】构建Softmax回归模型
(建立一个拥有一个线性层的softmax回归模型)
通过为输入图像和目标输出类别创建节点,开始构建计算图:
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
x,y_可以在Tensorflow运行某一计算时根据该占位符输入具体的值。
x:二维浮点数张量,shape为[None,784],784为一张展平的MNIST图片维度,None作为第一个维度值,指代batch大小(x数量不定).
y_:二维浮点数张量,每一行为一个10维的one-hot向量,代表某一MNIST图片类别。
(注:placeholder占位符的shape参数可选,但有了shape,Tensorflow可以自动捕捉因数据维度不一致导致的错误)

W = tf.Variable(tf.zeros([784,10]))
b = tf.Variable(tf.zeros([10]))
变量可以在计算过程中使用,甚至进行修改。
在机器学习的过程中,模型参数(模型需要每轮改变学习得到的参数值,有些参数值即为模型训练过后得到的结果)一般用变量Variable来表示。

sess.run(tf.initialize_all_variables())
变量需要通过session初始化后才可以使用,可一次性为所有变量完成此操作。

y = tf.nn.softmax(tf.matmul(x,W) + b)
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
注意,tf.reduce_sum把minibatch里的每张图片的交叉熵值都加起来了,我们计算的交叉熵是指整个minibatch的。

train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
用来向计算图中添加新操作,包括计算梯度,计算每个参数的步长变化,并计算新的参数值。
返回的train_step操作对象,在运行时会使用梯度下降来更新参数,因此,整个模型的训练可以通过反复地运行train_step来完成。

for i in range(1000):
  batch = mnist.train.next_batch(50)
  train_step.run(feed_dict={x: batch[0], y_: batch[1]})
每一步迭代加载50个训练样本,然后执行一次train_step,通过feed_dict将x和y_张量占位符用训练数据替代。
(注:在计算图中可以用feed_dict来替代任何张量,并不仅限于替换占位符)

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels})

【十五】创建多层卷积网络
1.权重初始化
为创建该模型,需要创建大量权重和偏置项,该模型中的权重在初始化时应该加入少量的噪声打破对称性以及避免0梯度。
由于使用的是ReLU神经元,因此较好的做法是用一个较小的正数初始化偏置项,以避免神经元节点输出恒为0的问题(dead neurons)。
为了不反复进行初始化操作,定义两个函数用于初始化:
def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

函数分析.
tf.truncated_normal(shape, mean, stddev):
shape表示张量维度,mean为均值,stddev为标准差。

 

 

该函数产生正态分布,均值和标准差由使用者自行设定,如果产生正态分布的值与均值差值大于两倍的标准差,则重新生成。

 

 

函数分析.
tf.constant(value, shape):
创建一个常量tensor,按照给出value来赋值,可以用shape来指定其形状,value可以是一个数,也可以是一个list:
如果是一个数,那么这个常量中所有值的按该数来赋值;

如果是list,那么len(value)一定要小于等于shape展开后的长度。赋值时,先将value中的值逐个存入,不够的部分全部存入value的最后一个值。

 

2.卷积、池化

 

对卷积和池化不了解的朋友们可以参看本人另外一篇博客:

机器学习中的一些基本概念(未完待续)

Tensorflow在卷积、池化上有很强的灵活性(包括怎样处理边界,步长应该设多大),在该实例中,一直使用vanilla版本。
卷积使用1步长(stride size)、0边距(padding size)的模板,保证输入输出是同一个大小。
池化用简单传统的2*2大小的模板作max pooling,为了代码更简洁,该部分被抽象成一个函数:
def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                        strides=[1, 2, 2, 1], padding='SAME')

函数分析.
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None):
除去name参数用以指定该操作的name,与该方法有关的参数一共有5个:
【input】
指需要做卷积的输入图像,要求是一个tensor,具有[batch,in_height,in_weight,in_channels]这样的shape,表示[训练时一个batch的图片数量,图片高度,图片宽度,图片通道数],这是一个四维的tensor,要求类型为float32或float64。

【filter】
相当于CNN中的卷积核,要求是一个tensor,具有[filter_height,filter_width,in_channels,out_channels]这样的shape,表示[卷积核的高度,卷积核的宽度,图像通道数,卷积核个数],要求类型与参数input相同,其中,第三维in_channels就是参数input的第四维。

【strides】
卷积在图像每一维的步长,一个一维向量,长度为4。

【padding】
string类型的量,只能是“SAME”、“VALID”其中之一,决定了不同的卷积方式。

【use_cudnn_on_gpu】
bool类型,是否使用cudnn加速,默认为true。

结果返回一个tensor,该输出即为我们常说的feature map。
 

Q.

图像如何转化成tensor中的数字?(像素点中存储的是什么?)

A.

像素点中存储的即为数字。
(例如,GBR方式存储0-255的数字)


函数分析2.
tf.nn.max_pool(value, ks计组据说是计算机专业最难的课程ize, strides, padding, name=None):
参数有4个,与卷积类似:
【value】
需要池化的输入,一般来讲,池化层接在卷积层后面,所以输入通常是feature map,依然是[batch,height,weight,channels]这样的shape。

【ksize】
池化窗口大小,取一个四维向量,一般是[1,height,width,1],因为不想在batch和channels上做池化,所以这两个维度设为1。

【strides】
和卷积类似,窗口在每个维度上滑动的步长,一般也是[1,stride,stride,1]。

【padding】
和卷积类似,可以取“VALID”或“SAME”。

结果返回一个tensor,类型不变,shape依然是[batch,height,weight,channels]这种形式。

 

3.第一层卷积

第一层:由一个卷积接一个max pooling完成。
卷积在每个5*5的patch中算出32个特征,卷积的权重张量形状是[5,5,1,32],前两个维度是patch的大小,接着是输入的通道数,最后是输出的通道数,对每个输出通道都有一个对应的偏移量。
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

为了用这一层,我们把x变成一个4d向量,其第2、第3维对应图片的宽、高,最后一维代表图片的颜色通道数。
(因为是灰度图所以这里的通道数为1,如果是rgb彩色图,则为3)。
x_image = tf.reshape(x, [-1,28,28,1])

我们把x_image和权值向量进行卷积,加上偏置项,然后应用ReLU激活函数,最后进行max pooling。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

4.第二层卷积
为了构建一个更深的网络,我们会把几个类似的层堆叠起来。
第二层中,每个5x5的patch会得到64个特征。
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

5.密集连接层
现在,图片尺寸减小到7x7,我们加入一个有1024个神经元的全连接层,用于处理整个图片。
我们把池化层输出的张量reshape成一些向量,乘上权重矩阵,加上偏置,然后对其使用ReLU。
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
 

6.Dropout

对dropout不了解的朋友们可以参看本人另外一篇博客:

机器学习中的一些基本概念(未完待续)

为了减少过拟合,在输出层之前加入dropout。
用一个placeholder代表一个神经元的输出在dropout中保持不变的概率,这样可以在训练过程中启用dropout而在测试过程中关闭它.Tensorflow的tf.nn.dropout操作除了可以屏蔽神经元的输出外,还会自动处理神经元输出值的scale,所以用dropout时可以不用考虑scale。
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

7.输出层
最后,添加一个softmax层,就像之前的单层softmax regression一样。
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

8.训练和评估模型
为了进行训练和评估,我们使用与之前简单的单层SoftMax神经网络模型几乎相同的一套代码。
只是我们会用更加复杂的ADAM优化器来做梯度最速下降,在feed_dict中加入额外的参数keep_prob来控制dropout比例.
每100次迭代输出一次日志。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
  batch = mnist.train.next_batch(50)
  if i%100 == 0:
    train_accuracy = accuracy.eval(feed_dict={
        x:batch[0], y_: batch[1], keep_prob: 1.0})
    print "step %d, training accuracy %g"%(i, train_accuracy)
  train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

print "test accuracy %g"%accuracy.eval(feed_dict={

    x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})

 

Tensorflow | FailedPreconditionError错误 错误日志: tensorflow.python.framework.errors_impl.FailedPreconditionError: 2 root error(s) found. (0) Failed precondition: Error while reading resource variable crf_1/left_boundary from Container: localhost. This could mean that the variable was uninitialize 阅读详情

相关推荐

浅入浅出TensorFlow 1 - 初识TensorFlow

一. TensorFlow 的发展历程        OpenCV 可能是多数人都很熟悉的计算机视觉库,参考下图机器学习库的发展轴线。        近几年,随着深度学习技术的快速发展,诞生了 Torch、Theano、Caffe、MxNet 等一批深度学习库,而 TensorFlow 正是这些框架中的佼佼者,得益于其 优秀的架构设计理念及工程实现,以及丰富的学习资料,目前 TensorFlo

跟随技术的脚步-linolzhang的专栏 3991

tensorflow.python.framework.errors_impl.FailedPreconditionError: Could not find variable Variable.

Could not find variable Variable.

qq_47056525的博客 3670

AI:初识 TensorFlow

Google 出品的深度学习框架TensorFlow,让你用最轻松的方式打开 AI 大门。

Kant2048的博客 963

tensorflow.python.framework.errors_impl.FailedPreconditionError: Attempting to use uninitialized val

问题:使用tf.global_variables_initializer()时报错,代码如下: import tensorflow.compat.v1 as tf tf.disable_v2_behavior() # 变量是一种特殊的张量,变量中存的值是张量 # 变量可以进行持久化保存,张量不可以 # 变量使用之前,要进行显式初始化 a = tf.constant([1, 2, 3, 4]) # 定义变量 先进行全局初始化(初始化也是一个OP,需要在session的run下执行) init_op =

weixin_45231460的博客 3811

零基础菜鸟的Python+TensorFlow之旅(一)——初识Tensor & Flow

由于TensorFlow中的一些设计理念比较先进,所以很多同学会在一些知识的理解上产生困扰,本人作为一名从教师转行的程序猿,深知各位同学在这条道路上的难言之痛,于是决定开设此教程,为大家提供最简单最直接最粗暴的讲解,帮助各位同学以最轻松最愉快最爽朗的方式学会TensorFlow这个必备大杀器!

寻找编程之光 3万+

初识TensorFlow:从入门到简单应用

好啦,朋友们,上面这些就是TensorFlow的一些基本用法啦。从构建简单的线性回归模型、神经网络分类模型,到模型的保存与加载,这些都是使用TensorFlow的基础操作。就像搭积木一样,你可以用这些基础的操作构建出各种各样复杂的深度学习模型。小伙伴们,你们有没有用TensorFlow做过一些有趣的项目呢?或者在使用过程中遇到了什么问题呢?欢迎在评论区留言讨论哦。

Kenneth55的专栏 891

tensorflow.python.framework.errors_impl.FailedPreconditionError: Could not find variable var.

今天在使用tensorflow操作变量的时候,报了如下错误 用百度翻译查了一下报错信息如下 报错信息提示变量未初始化 解决方法如下,在报错的代码前面添加如下初始化即可。 高版本tensorflow 低版本tensorflow init = tf.global_variables_initializer() session.run(init) ...

坚持不懈的大白的博客 8429

人工智能TensorFlow-初识TensorFlow

转眼要接近2018年的尾巴了。感觉一年忙忙碌碌,所得甚少。也许是不能知足常乐的原因吧。 这次决定涉足人工智能领域,因为认识到该领域的强大,便希望能与量化交易方面相结合,以达到更理想的效果。 计划在年底前,熟练使用TensorFlow。预祝未来一年的自己,变得更好吧~ TensorFlow 是一个用于人工智能的开源神器 TensorFlow 中的tensor是张量的意思,flow是流动的意思。Te...

东南有大树的博客 536

Tensorflow新手通过PlayGround可视化初识神经网络

Tensorflow新手通过PlayGround可视化初识神经网络 2018年10月7日 0条评论560次阅读4人点赞 原文链接 :https://finthon.com/tensorflow-playground-nn/ 是不是觉得神经网络不够形象,概念不够清晰,如果你是新手,来玩玩PlayGround就知道,大神请绕道。 PlayGround是一个在线演示、实验的神经网络平...

胭脂草的ABC博客 1346

Design Compiler时序约束实战:如何用set_clock_uncertainty解决时钟抖动问题?

本文深入探讨了在Design Compiler时序约束实战中,如何精准运用set_clock_uncertainty命令解决时钟抖动等非理想性问题。文章详细解析了该命令在时钟树综合前后的不同应用策略、建立时间与保持时间的差异化设置,并对比了其与set_clock_jitter的适用场景,旨在帮助数字IC设计工程师制定稳健的时序约束,为后端物理实现预留合理的安全裕量,从而高效达成时序收敛。

weixin_29196891的博客 299

Ubuntu终端AI编程助手配置:Claude Code接入DeepSeek模型全攻略

在软件开发领域,AI编程助手正成为提升开发效率的重要工具。其核心原理是通过大语言模型理解自然语言指令,生成、解释或调试代码。这类工具的技术价值在于将AI能力无缝集成到开发工作流中,减少上下文切换,实现更流畅的人机协作。常见的应用场景包括代码生成、错误调试、代码重构和技术问答等。本文聚焦于在Ubuntu系统中,将流行的终端AI助手Claude Code的后端模型从原版Claude替换为DeepSeek。通过配置环境变量实现API端点和模型的映射,这一过程涉及Node.js环境管理、npm包安装和关键环境变量设

weixin_42527654的博客 376

初识TensorFlow

使用 TensorFlow, 你必须明白 TensorFlow: Tensorflow有一下几个简单的步骤: 使用 tensor 表示数据. 使用图 (graph) 来表示计算任务. 在会话(session)中运行图s 关于新版本 TensorFlow提供多种API。最低级API为您提供完整的编程控制。请注意,tf.contrib.learn这样的高级API可以帮助您管理数据集,估计器,...

ysl的博客 263

光学设计必看:如何用泽尼克系数优化镜头像差?从Zemax到实际案例解析

本文深入解析了如何利用泽尼克系数优化镜头像差,从理解其工程价值、解码软件报告,到在Zemax中主动设置优化目标。通过高倍显微物镜等实际案例,详细展示了如何通过分析泽尼克系数诊断问题,并实施针对性优化策略,从而实现对球差、彗差等像差的精准控制,提升光学设计的精细度和系统稳健性。

gg901的博客 1025

深度学习-初识Tensorflow框架、Keras框架(上篇笔记)

这篇文章主要记述了博主安装Tensorflow,,Keras的过程,其中遇到的问题,并以MINIST(基于Tensorflow)和CNN图片分类(基于Keras)学习两种框架的使用,最后对比一下两种框架以便在后期的学习更好的选择。

weixin_42036617的博客 945

【树莓派4B】八、树莓派软路由,Ubuntu18.04配置路由转发

我的目的是通过把树莓派做成一个软路由,相当于一个智能路由器。 材料 树莓派4B,操作系统其实可以是其他的Linux版本。以为4B自带无线网卡,就用自带无线网卡作为路由出口。 大概思路 具体步骤 配置两个网卡的IP sudo vim /etc/network/interface 这里设置静态IP,也可以动态获取。 #Local loopback auto lo iface lo inet loopback #Ethernet0 auto eth0 iface eth0 inet static addr

林愿留的博客 10万+

半导体制造中的通孔缺陷与冗余通孔技术解析

通孔(Via)是半导体制造中连接不同金属层的关键结构,其可靠性直接影响芯片良率。随着工艺节点演进至28nm及更先进制程,通孔缺陷导致的良率损失日益显著,主要表现为完全开路和高阻态两种失效模式。冗余通孔技术通过在关键位置增加备用连接,可有效提升芯片可靠性,其实现涉及物理冗余与电气冗余两种方案。该技术在7nm/5nm工艺中已证明可将通孔相关缺陷率降低10-100倍,但面临设计规则复杂性、规模处理效率和动态合规验证等工业化挑战。智能双切通孔插入流程通过动态DRC规则解析、奇偶层级并行处理和增量式验证等创新方法,显

weixin_35982453的博客 381

TensorFlow——初识TensorFlow

TensorFlowMINST MINST MINST是一个非常简单的机器视觉数据集,它由几万张28像素*28像素的手写数字组成,这些图片只包含灰度值信息。如下图所示:

Digger的博客 299

【BUG已解决】RuntimeError: CUDA error: no kernel image is available for execution on the device 解决方案

方案适用场景推荐指数精确匹配官方版本绝大多数场景(大众消费卡+主流数据中心卡)⭐⭐⭐⭐⭐安装历史旧版本Pascal/Maxwell等老显卡⭐⭐⭐⭐安装Nightly版本刚发布的最新架构显卡⭐⭐⭐⭐源码编译官方各版本均不支持的特殊硬件⭐⭐Docker官方镜像追求省心、避免版本排查⭐⭐⭐⭐⭐的核心是PyTorch 预编译包不包含你显卡对应的计算能力架构先确认显卡真实计算能力),而不是盲目重装驱动对比 PyTorch 支持的架构列表),定位是否真的不匹配精确匹配官方版本。

博客 1万+

Tensorflow |(1)初识Tensorflow

关于 TensorFlow TensorFlow是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。它灵活的架构让你可以在多种平台上展开计算,例如台式计算机中的一个或多个CPU(或GPU),服务器,移动设备等等。TensorFlow 最初由Goo...

DrugOne 1412

【Dify(v1.x) 核心源码深入解析】File 模块

文件模型(File Model)定义了文件的核心属性,如文件类型、传输方法、相关标识符等,是整个文件处理流程的数据基础。文件解析器(UploadFileParser)负责解析上传的文件,根据配置决定以何种方式(URL 或 Base64)提供图像数据。工具文件解析器(ToolFileParser)专注于工具文件的管理,提供工具文件管理器的获取接口。签名验证与生成(helpers.py)包含文件 URL 签名的生成与验证逻辑,确保文件访问的安全性。文件管理器(file_manager.py)

666

零基础入门simulink1000例--第112例:在 Simulink 中构建一个基本的相控阵雷达波束形成系统仿真模型

摘要:本文详细介绍了使用Simulink实现相控阵雷达波束形成技术的完整流程。从创建Simulink模型、设计天线阵列、信号生成,到波束形成算法实现和结果可视化,涵盖了关键步骤。文章重点讲解了均匀线性阵列的设计、线性调频信号生成、相位偏移计算等核心技术,并提供了MATLAB代码示例。通过Scope或ArrayPlot模块进行结果分析,最终可导出为嵌入式代码部署。该系统展现了电子扫描波束的技术优势,为雷达系统设计提供了有效的仿真方法。

amy_mhd的博客 209

初识TensorFlow Lite

1.摘要 近年来,随着移动设备和应用的普及,及其软硬件的提升。另一方面,机器学习在近些年来也取得了不小成果。机器学习需要大量数据,而移动设备存在也产生大量数据,移动设备也需要机器学习的成果。由此出现了机器学习与移动设备的碰撞,在移动设备上使用、应用机器学习的成果是很有必要的。 2.应用背景 视觉在人与人交互以及人与自然界的交互过程中起到非常重要的作用,让终端设备具有智能的视觉识别和交互能力是人工智能和计算机技术的核心挑战和远大目标之一。 在早些年,全球物联网的设备数,就已经超...

qq_36544007的博客 947

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

初识 Tensorflow.js【Plan 6】

TensorFlow.js是Google开发的JavaScript机器学习库,支持在浏览器和Node.js环境中训练部署模型。它提供张量运算、神经网络构建、模型训练等核心功能,支持加载预训练模型和转换Python模型。本文介绍了TensorFlow.js的核心概念、安装方法、基本操作(张量运算、模型构建)、内存管理技巧,以及MobileNet轻量级模型的特点。开发者可利用这些工具快速实现浏览器端的图像分类、实时预

kuiini的博客 1257

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

初识tensorflow

下载并安装 TensorFlow 2.0 测试版包。 import tensorflow as tf # 载入MNIST数据集 mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() # 将样本从整数转换为浮点数 x_train, x_test = x_train / 255.0, x_test / 255.0 # 将模型的各层堆叠起来,搭建Sequential模型 model =

MusicDancing的博客 4506
上一篇: YOLOv2简述
下一篇: 物体检测算法SSD简述
echoKangYL
博客等级 码龄9年 432粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值