runtime无法执行grep_Caffe和py-faster-rcnn日常使用备忘录

这篇博客详细记录了在使用Caffe和py-faster-rcnn过程中遇到的问题及解决方案,涵盖了从训练、日志解析、绘图到调试的各个环节,包括训练loss曲线绘制、protobuf版本问题、numpy错误、caffe模型初始化一致性等,并提供了具体的代码示例和修改建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

罗列日常使用中遇到的问题和解决办法。包括:

{

caffe使用中的疑惑和解释;

无法正常执行 train/inference 的情况;

Caffe基础工具的微小调整,比如绘loss曲线图;

调试python代码技巧,基于vscode;

py-faster-rcnn在自己数据集上调参技巧

py-faster-rcnn因为numpy版本、自己数据集等各种原因导致的坑和解决办法

py-faster-rcnn本身细节的各种坑

调试matcaffe的技巧

protobuf版本的坑

...

}

保持更新。

last update: 2018.05.13 18:59 GTC

test/deploy阶段的Accuracy层和Softmax层

拿AlexNet举例,为什么计算Accuracy时使用fc8和label这两个blob作为输入,而deploy时fc8这个blob要经过Softmax层再作为结果输出?

解释:fc8相当于定性比较,显示了网络对各个类别上的相对置信度(“相对概率”)

经过softmax是做了归一化操作,是严格服从概率的归一化性质的。

而在Accuracy的计算过程中,只需要知道置信度最大的那个类别,用argmax求出来,所以不需要严格的概率结果(softmax产出的prob),只需要相对置信度向量(fc8)即可。

解析py-faster-rcnn训练的日志,绘制loss曲线

1. 准备文件

拷贝caffe/tools/extra目录下的:

parse_log.sh

extract_seconds.py

plot_training_log.py.example (复制后去掉.example)

到训练产生的log文件所在目录。

2.修改代码

extract_seconds.py 38行:

#if line.find('Solving') != -1:

if line.find('Initializing') != -1:

parse_log.sh 29行:

# grep '] Solving ' $1 > aux3.txt

grep 'net.cpp:' $1 > aux3.txt

修改后,能保证正常log的解析,也能用来解析py-faster-rcnn训练的日志,而不再报错提示说'Start time not found'

3.个性化定制

比如网络有多个loss,需要分开绘图。修改parse_log.sh中,利用grep和awk获取的loss等数据。默认只有training loss。我的改成这样:

# grep '] Solving ' $1 > aux.txt

grep 'Initializing solver' $1 > aux.txt

grep ', loss = ' $1 >> aux.txt

grep 'Iteration ' aux.txt | sed 's/.*Iteration \([[:digit:]]*\).*/\1/g' > aux0.txt

grep ', loss = ' $1 | awk '{print $9}' > aux1.txt

grep ', lr = ' $1 | awk '{print $9}' > aux2.txt

## 新提取4个loss

grep ' loss_bbox = ' | awk '{print $11}' > aux4.txt

grep ' loss_cls = ' | awk '{print $11}' > aux5.txt

grep ' rpn_cls_loss = ' | awk '{print $11}' > aux6.txt

grep ' rpn_loss_bbox = ' | awk '{print $11}' > aux7.txt

# Extracting elapsed seconds

$DIR/extract_seconds.py aux.txt aux3.txt

# Generating. 将新增列添加列名称

echo '#Iters Seconds TrainingLoss loss_bbox loss_cls rpn_cls_loss rpn_loss_bbox LearningRate'> $LOG.train

# paste aux0.txt aux3.txt aux1.txt aux2.txt | column -t >> $LOG.train

paste aux0.txt aux3.txt aux1.txt aux4.txt aux5.txt aux6.txt aux7.txt aux2.txt | column -t >> $LOG.train

rm aux.txt aux0.txt aux1.txt aux2.txt aux3.txt aux4.txt aux5.txt aux6.txt aux7.txt

然后修改plot_training_log.py。这个代码主要是调用parse_log.sh提取数据到xxxx.log.train中,xxxx.log是你指定的训练所得log文件;而其他代码则显得冗余,我反正忽略了,自行绘图了。改成这样:

#!/usr/bin/env python

# coding:utf-8

"""

默认的模板写的太复杂。简单点。步骤包括:

1. 调用shell脚本,利用grep等命令得到xxx.log.train文件,里面全都是整理好的各列数据

2. 利用matplotlib/ggplot/visdom等工具绘图

"""

import inspect

import os

import random

import sys

import matplotlib.cm as cmx

import matplotlib.colors as colors

import matplotlib.pyplot as plt

import matplotlib.legend as lgd

def get_parsed_data(log_fname):

"""

@description

执行shell脚本来解析xx.log到xx.log.train;从这个xx.log.train文件读取数据到变量并返回

需要保证parse_frcnn_log.sh脚本和xx.log在同一个目录下

"""

os.system('%s %s' % (get_log_parsing_script(), log_fname))

# 从xxx.log.train读取数据

data_file_name = log_fname + '.train'

fin = open(data_file_name)

lines = [_.rstrip() for _ in fin.readlines()]

fin.close()

column_names = lines[0].split(' ')

n = len(column_names)

data = []

for i in range(n):

data.append([])

for line in lines[1:]:

t = line.split()

for i in range(n):

data[i].append(t[i])

return column_names, data

def get_log_parsing_script():

dirname = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))

return dirname + '/parse_frcnn_log.sh'

def plot_frcnn_data(log_fname):

column_names, data = get_parsed_data(log_fname)

"""

以下是我自己的绘图语句,5个loss-iter曲线在一个figure中绘制。你也可以分别绘制

"""

# ax = plt.subplot2grid(shape, loc)

ax0 = plt.subplot2grid((2,4), (0,0), colspan=2, rowspan=2)

ax1 = plt.subplot2grid((2,4), (0,2))

ax2 = plt.subplot2grid((2,4), (0,3))

ax3 = plt.subplot2grid((2,4), (1,2))

ax4 = plt.subplot2grid((2,4), (1,3))

ax0.plot(data[0], data[2])

ax1.plot(data[0], data[3])

ax1.set_title(column_names[3]+' vs. Iters')

ax2.plot(data[0], data[4])

ax2.set_title(column_names[4]+' vs. Iters')

ax3.plot(data[0], data[5])

ax3.set_title(column_names[5]+' vs. Iters')

ax4.plot(data[0], data[6])

ax4.set_title(column_names[6]+' vs. Iters')

plt.show()

if __name__ == '__main__':

log_fname = 'faster_rcnn_end2end_VGG16_.txt.2018-03-14_01-12-12.log'

plot_frcnn_data_try(log_fname)

绘制验证集上的精度曲线

在分类任务的各种教程里,都有让在validation集上绘制accuracy-iteration曲线,如果它越来越高就说明训练的好,而且曲线应当没有太大的“反弹”(fluctuations)。

但是在目标检测任务上,各种大牛的论文和小白的教程,都故意忽略这种调试方式。个人训练了RPN网络,每间隔500迭代存储一个模型,在验证集上测试得到AP(Average Precision),绘制AP-Iteration曲线。

实际情况:曲线的fluctuations很大,可能前一次是16%的AP,下一次可以跌到8%;我自己的数据集和VOC2007的都有这样的波动。估计太难看了,大家都不在发表的东西上绘制出来。

Caffe自带绘制网络图的工具,使用报错

执行命令:python draw_net.py ~/work/caffe/models/bvlc_googlenet/train_val.prototxt ~/work/caffe/models/bvlc_googlenet/train_val.png --rankdir=LR

报错:'google.protobuf.pyext._message.RepeatedScalarConta' object has no attribute '_values'

解决:removed the "._values" on lines 94,96,98 in python/caffe/draw.py. Looks like it wants to get the length of the array. I worked by remove them

ref:https://github.com/NVIDIA/DIGITS/issues/591

训练时出现: check failure stack trace

有博客提到是路径错误。

我的解决办法是,不要用shell调用python代码,而是直接执行python的训练代码。(我是py-faster-rcnn修改时遇到;C++版本的训练?不知道)

Check failed: status == CUBLAS_STATUS_SUCCESS (1 vs 0)

帮阿毛在Mint系统上运行RCF这个边缘检测算法的代码时,网络加载的最后提示这句。注意,错误码是1而不是11,这个问题比较少见。Mint是基于ubuntu的改造版。

解决办法:

rm ~/.nv

然后重新运行代码

参考:BVLC/caffe#5564

pycaffe引入caffe报错

看到很多做法是设置PYTHONPATH这个环境变量。其实个人不推荐。原因:很多基于caffe的算法代码,自行修改过caffe代码,导致你的系统上有多个版本的caffe。如果用PYTHONPATH,那么如何区分这些版本的caffe?很容易冲突。

解决办法:在需要import caffe的代码头部:

pycaffe_dir = '/home/chris/work/caffe-BVLC/python'

import sys

sys.path.insert(0, pycaffe_dir)

或者,干脆写一个专门用来导入pycaffe路径的文件init_paths.py:

pycaffe_dir = '/home/chris/work/caffe-BVLC/python'

import sys

sys.path.insert(0, pycaffe_dir)

然后,每次需要import caffe的代码,先import init_paths

VSCODE调试python代码

不一定是pycaffe,但是处理数据什么的,用python,也需要debug。首先装python插件。

需要输入参数来执行的python代码的调试

python debug的配置文件launch.json中,添加args关键字段和具体取值

使用到相对路径,但是该python代码不在打开的目录顶层,而是在子目录中,导致调试运行时路径找不到。

launch.json中修改cwd字段。是current working directory的意思。

具体参考如下:

...

"version": "0.2.0",

"configurations": [

{

"name": "Python: Current File",

"type": "python",

"request": "launch",

"stopOnEntry": true,

"pythonPath": "${config:python.pythonPath}",

"program": "${file}",

"cwd": "${workspaceFolder}/vis/plot_loss", // 把python调试所使用的工作目录,调到vis/plot_loss子目录

"env": {},

"envFile": "${workspaceFolder}/.env",

"debugOptions": [

"RedirectOutput"

],

"args": [ // args关键字,指定程序输入的参数。 如果是"--key val"类型的,分拆为"--key", "val"

"6",

"loss_vs_iter.png",

"coolnet.log"

]

},

...

关闭终端输出的log信息

使用已经训练好的caffemodel来执行inference时,我不需要看到log信息,而只想看到我需要的计算结果。比如跑faster-rcnn的测试,我只想看到整个数据集上的AP结果输出,而不要看到网络建立的各种信息。

pycaffe下的解决办法:在当前执行的python脚本文件import部分,第一次(包含间接的)import caffe前,使用这三行:

import os

os.environ['GLOG_minloglevel'] = '3'

import caffe

所谓间接的import caffe,就是例如import fast_rcnn.test_net,而fast_rcnn/test_net.py中有import caffe。

AttributeError: 'module' object has no attribute 'text_format'

在文件./lib/fast_rcnn/train.py增加一行import google.protobuf.text_format 即可解决问题

原因是用的protobuf的pip包的版本升级到2.6了,原本是2.5版不需要这样import的。或者,也可以通过指定安装2.5版本的protobuf的pip包来解决:sudo pip install protobuf==2.5.0

Caffe中常见层的常见参数默认值

lr_mult: 1

decay_mult: 1

测试精度突然降低为0

在训练RPN检测网络,自己的数据集,训练结束后每隔500迭代的caffemodel执行一次测试(算出AP),前面6000次(2个epoch)AP在5%~20%之间,第6500次的AP突然降低到0。训练期间learning rate没有降低过。

原因:梯度爆炸,某次forward时候产生过大的梯度,对应的loss值反常的大。

解决办法:增大batch size来平滑。通过solver中添加iter_size:2改进。另,有人提到clip_gradient,但感觉取值很难确定,不好玩。

调整参考窗口大小

Anchor机制下,指定Anchor box的scale和aspect ratios,生成rpn_conv上的参考窗口;这些窗口再乘以base_size,就放大(映射)为网络输入图上的参考窗口。

当待检测目标很小(比如平均只有26像素),但是Anchor scale很大(使用[8, 16,32]),那么参考窗口会很大(仅考虑1:1方形,base size=16, 参考窗尺寸[128, 256,512]),按说,和gt_boxes的IoU,不是0就是0.01这么小。overlap这么小,小于0.7这个预设值,不是好的正样本;但是RPN里,还设定了和每个gt_box的最大overlap的参考窗口,也作为正样本:

# fg label: for each gt, anchor with highest overlap

labels[gt_argmax_overlaps] = 1

也就是:参考窗口和gt明明IoU只有0.01,但是被拉过来做为正样本了!矬子里拔将军,显然是Garbage In, Garbage Out了,不可能产生好的训练结果。

这时候虽然开启anchor_target_layer.py的Debug模式,看到正负样本比例接近1:1,但显然正样本可能都是负样本。。

训练faster-rcnn出现错误“KeyError: 'max_overlaps'”解决

原因可能有多种,最可能的是:缓存文件出错。也即:训练使用的annotation内容更新过了;或者使用了别的数据集训练了。

把py-faster-rcnn/data/cache目录清空;把py-faster-rcn/data/VOCdevkit2007/annotations_cache清空

有时候觉得这个cache机制反而很蠢,埋藏错误。

去除各种随机过程,让同样参数配置下训练结果一致

严格的完全一致没有必要,这里只做到每次loss输出的小数点后2位能一致就可以了。

做这个去除随机过程的初衷是,在自己数据集上训练,如果迭代几万次,结果AP可以稳定一些,但是要等很久;如果只迭代几千次,比如3个epoch,9000iteration,每隔500iter测试全体测试数据计算出AP,绘制AP-Iteration曲线,发现这个曲线想当不稳定,同一个迭代次上AP可能相差10个点。

为了避免这种情况,固定样本出现顺序、固定网络初始化模型,基本锁定了大的变化。

固定样本出现顺序:

lib/roi_data_layer/layer.py有两处:

# self._perm = np.random.permutation(np.arange(len(self._roidb)))

self._perm = np.arange(len(self._roidb))

固定网络初始化模型:

copy_from()会从同名层复制,预训练模型中没有的层,则初始化,每次初始化服从的分布一样但是具体数值不一样。

创建一个RPN/FRCNN网络,不执行训练,直接保存。以后用这个模型初始化即可。

mk_init_model.py:

# coding:utf-8

import _init_paths

import caffe

from fast_rcnn.config import cfg, cfg_from_file

if __name__ == '__main__':

cfg_file = 'experiments/cfgs/myconfig.yml'

cfg_from_file(cfg_file) #这一步很重要,否则网络都建立不起来,因为第一层数据层的建立需要各种cfg的判断

prototxt = 'models/mydb/rpn/zf/train.pt'

pretrained_model = 'data/imagenet_models/ZF.v2.caffemodel'

save_pth = 'data/imagenet_models/rpn_zf_init.caffemodel'

net = caffe.Net(prototxt, caffe.TRAIN)

net.copy_from(pretrained_model)

net.save(save_pth)

TypeError: 'numpy.float64' object cannot be interpreted as an index

因为numpy版本高导致的。个人倾向于改代码而不是降低numpy版本。

/home/xxx/py-faster-rcnn/lib/roi_data_layer/minibatch.py

将第26行:fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)

改为:fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image).astype(np.int)

2)/home/xxx/py-faster-rcnn/lib/datasets/ds_utils.py

将第12行:hashes = np.round(boxes * scale).dot(v)

改为:hashes = np.round(boxes * scale).dot(v).astype(np.int)

/home/xxx/py-faster-rcnn/lib/fast_rcnn/test.py

将第129行: hashes = np.round(blobs['rois'] * cfg.DEDUP_BOXES).dot(v)

改为: hashes = np.round(blobs['rois'] * cfg.DEDUP_BOXES).dot(v).astype(np.int)

4)/home/xxx/py-faster-rcnn/lib/rpn/proposal_target_layer.py

将第60行:fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image)

改为:fg_rois_per_image = np.round(cfg.TRAIN.FG_FRACTION * rois_per_image).astype(np.int)

TypeError: slice indices must be integers or None or have an index method

依然是numpy版本问题,依然推荐改代码:

修改 /home/lzx/py-faster-rcnn/lib/rpn/proposal_target_layer.py,转到123行,原来内容:

for ind in inds:

cls = clss[ind]

start = 4 * cls

end = start + 4

bbox_targets[ind, start:end] = bbox_target_data[ind, 1:]

bbox_inside_weights[ind, start:end] = cfg.TRAIN.BBOX_INSIDE_WEIGHTS

return bbox_targets, bbox_inside_weights

修改为:

for ind in inds:

ind = int(ind)

cls = clss[ind]

start = int(4 * cls)

end = int(start + 4)

bbox_targets[ind, start:end] = bbox_target_data[ind, 1:]

bbox_inside_weights[ind, start:end] = cfg.TRAIN.BBOX_INSIDE_WEIGHTS

return bbox_targets, bbox_inside_weights

ValueError: operands could not be broadcast together with shapes (84,1024) (8,1)

完整的报错位置:

File "/home/py-faster-rcnn/tools/../lib/fast_rcnn/train.py", line 73, in snapshot

self.bbox_stds[:, np.newaxis])

ValueError: operands could not be broadcast together with shapes (84,1024) (8,1)

现象:在保存caffemodel文件时报错

修改:train.prototxt中类别数量忘记修改了,84对应着20类的pascal voc原版,8对应着自己的单个类别数据集(比如行人检测/人脸检测)

bbox_transform.py:48: RuntimeWarning: overflow encountered in exp

完整报错:

I0408 14:59:36.903856 16481 solver.cpp:229] Iteration 0, loss = 16.1743

I0408 14:59:36.903894 16481 solver.cpp:245] Train net output #0: loss_bbox = 0.169268 (* 1 = 0.169268 loss)

I0408 14:59:36.903903 16481 solver.cpp:245] Train net output #1: loss_cls = 17.8601 (* 1 = 17.8601 loss)

I0408 14:59:36.903909 16481 solver.cpp:245] Train net output #2: rpn_cls_loss = 7.35363 (* 1 = 7.35363 loss)

I0408 14:59:36.903914 16481 solver.cpp:245] Train net output #3: rpn_loss_bbox = 0.271347 (* 1 = 0.271347 loss)

I0408 14:59:36.903920 16481 sgd_solver.cpp:106] Iteration 0, lr = 0.001

/opt/shiyan/py-faster-rcnn/lib/fast_rcnn/bbox_transform.py:58: RuntimeWarning: overflow encountered in exp

pred_w = np.exp(dw) * widths[:, np.newaxis]

/opt/shiyan/py-faster-rcnn/lib/fast_rcnn/bbox_transform.py:58: RuntimeWarning: overflow encountered in multiply

pred_w = np.exp(dw) * widths[:, np.newaxis]

/opt/shiyan/py-faster-rcnn/lib/fast_rcnn/bbox_transform.py:59: RuntimeWarning: overflow encountered in exp

pred_h = np.exp(dh) * heights[:, np.newaxis]

/opt/shiyan/py-faster-rcnn/lib/fast_rcnn/bbox_transform.py:59: RuntimeWarning: overflow encountered in multiply

pred_h = np.exp(dh) * heights[:, np.newaxis]

('anchor_target_layer: avg_num_pos=', 9, 'avg_num_neg=', 246)

('proposal_target_layer: avg_num_pos=', 8, 'avg_num_pos=', 67, 'ratio: 0.132')

[1] 16481 floating point exception tools/train_net.py --gpu 0 --solver models/bdcirpn/frcnn/res101_np/solver.pt

按照github上这个issue, meetshah1995的评论我有用到,也就是:

在config.py中添加:

__C.BBOX_XFORM_CLIP = np.log(1000. / 16.)

and add these lines just before the predict_ctr_x is computed in your bbox_transform.py:

# Prevent sending too large values into np.exp()

dw = np.minimum(dw, cfg.BBOX_XFORM_CLIP)

dh = np.minimum(dh, cfg.BBOX_XFORM_CLIP)

这时候重新执行训练,报错变成了:

I0408 15:02:49.634021 17637 solver.cpp:229] Iteration 0, loss = 16.4431

I0408 15:02:49.634066 17637 solver.cpp:245] Train net output #0: loss_bbox = 0.172947 (* 1 = 0.172947 loss)

I0408 15:02:49.634074 17637 solver.cpp:245] Train net output #1: loss_cls = 18.3941 (* 1 = 18.3941 loss)

I0408 15:02:49.634080 17637 solver.cpp:245] Train net output #2: rpn_cls_loss = 7.35363 (* 1 = 7.35363 loss)

I0408 15:02:49.634088 17637 solver.cpp:245] Train net output #3: rpn_loss_bbox = 0.271347 (* 1 = 0.271347 loss)

I0408 15:02:49.634109 17637 sgd_solver.cpp:106] Iteration 0, lr = 0.001

[1] 17637 floating point exception tools/train_net.py --gpu 0 --solver models/bdcirpn/frcnn/res101_np/solver.pt

通过一番debug找到问题了,是因为产生了空的proposals,大坑啊,一堆边长为1的proposal,无论如何在_filter_boxes()之后肯定一个都不留啊。解决方法:把原来的:

# proposal_layer.py

keep = _filter_boxes(proposals, min_size * im_info[2])

proposals = proposals[keep, :]

scores = scores[keep]

换成:

# proposal_layer.py

keep = _filter_boxes(proposals, min_size * im_info[2])

if len(keep)!=0:

proposals = proposals[keep, :]

scores = scores[keep]

然而,虽然训练不会断了,但是loss直接变成nan了。显然上述修改还是有问题。解决方法:调小learning rate

"/usr/bin/ld: cannot find -lboost_python"

编译caffe时候遇到这个问题。

解决办法:

sudo apt install libboost-all-dev

MatCaffe创建网络失败,莫名其妙崩溃

通常出现在调试老版本的matcaffe。比如看中了一个论文的算法,其作者开源的代码基于matcaffe,但是caffe版本很老,而且作者的代码中各种bug,比如路径写死了但是readme中又不说,比如网络prototxt文件里面不兼容。。。

说一下我的调试技巧吧。如果matcaffe的网络建立时就报错,那么尝试用python接口去建立该网络,然后能在终端看到错误输出,而不是傻逼matlab什么都看不到就给一个窗口说,出现了问题是否要发送邮件,blablabla。

# fuck_matcaffe.py

import sys, os

sys.path.insert(0, '/opt/work/caffe-BVLC-cvprw15')

import caffe

prototxt = 'deploy.prototxt'

caffemodel = 'mynet.caffemodel'

net = caffe.Net(prototxt, caffemodel, caffe.TEST)

运行:

python fuck_matcaffe.py

CMake编译Caffe

不想吐槽官方Caffe了,一个安装程序都写不好。。

Ubuntu16.04, 使用CMake编译,要修改CAFFE_ROOT/cmake/Dependencies.cmake,添加boost库里面的regex:

find_package(Boost 1.54 REQUIRED COMPONENTS system thread filesystem regex)

否则编译到92%会提示:

../lib/libcaffe.so.1.0.0: undefined reference to `boost::re_detail::cpp_regex_traits_implementation::transform_primary(char const, char const) const'

以及添加自己编译的opencv路径(如果需要的话):

list(APPEND CMAKE_PREFIX_PATH "/opt/usr/opencv-git-3.4")

用PyCaffe生成prototxt,但是0.01这样的小数位数不准?

在利用pycaffe生成prototxt时用到了浮点数,比如指定conv1的初始化为std=0.01的高斯分布;查看生成的prototxt,发现0.01变成了0.009999999235这样的“不准确”数字。这个问题在protobuf版本为3.5.2时出现,在protobuf为3.3.0版本时消失(也就是准确的0.01)。似乎用sudo pip install protobuf==3.3.0就可以解决问题?

做了另一个尝试:加载prototxt文件,然后新保存为新文件。在初始的prototxt中,浮点数是正常的0.01这种,但在另存的prototxt中依然不准确。原因:protobuf前后端不一致导致的。

protobuf本身是C++写的。用pip装的protobuf是python接口。protobuf的Python接口可以用python自身的数据类型,也可以用c++的数据类型,默认是C++作为后端。因此切换后端为python即可。如何切换?通过环境变量:

export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python

python my_code.py

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值