win10 + bazel-0.20.0 + tensorflow-1.13.1 编译tensorflow GPU版本的C++库

本教程详细介绍了在Win10系统下,使用Bazel、CUDA10.0和cuDNN7.6编译TensorFlow 1.13.1 GPU版本的C++库的全过程。涵盖了所需软件安装、配置、编译步骤及常见问题解决,最后展示了如何在C++项目中调用编译后的库。

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

参考资料:

---------------------- 超长文多图预警!!! ----------------------

安装所需软件/库

Step1. 安装vs2015,CUDA 10.0和cuDNN 7.6

vs2015的安装这里就不多说了,Anaconda3、CUDA和cuDNN的安装可参考cmake编译opencv: Win10+cmake3.14.4+cuda10.0+cudnn7.6+opencv-4.0.0+contrib+vs2015

Step2. 安装msys2

进入msys2官网,选择msys2-x86_64-20190524.exe,或直接点击此链接: msys2-x86_64-20190524,进行下载。

在这里插入图片描述
下载好后开始安装,点击Next

在这里插入图片描述
接下来选择安装路径,我个人为了避免安装在系统盘,将盘符改成了D盘,如下图。然而在之后的编译过程中发现,msys64会在C盘再安装一遍,所以这里推荐使用默认,即安装在 C:\msys64 下。接着点击Next,进入下一步。

在这里插入图片描述
这里直接Next

在这里插入图片描述
软件开始安装,稍微等一会儿就好了。

在这里插入图片描述
安装完成,点击Finish

在这里插入图片描述
弹出一个类似cmd的命令窗口,输入一下命令:

pacman -Syu

询问是否进行安装,输入y,回车:

在这里插入图片描述
第一个命令执行完毕:

在这里插入图片描述
接着输入以下命令:

pacman -S git

同样地,询问是否安装输入 y,然后回车:

在这里插入图片描述
第二个命令执行完毕:

在这里插入图片描述
接着输入:

pacman -S patch unzip grep

是否安装输入 y,回车:

在这里插入图片描述
第三个命令执行完毕:

在这里插入图片描述

软件装好以后,需要配置环境变量。
将以下路径添加到系统变量 Path 中:

D:\msys64
D:\msys64\usr\bin

注意,如果之前装在C盘,则需要进行相应修改。
如下图所示:

在这里插入图片描述
msys2安装配置完毕!

Step3. 安装bazel

进入github,选择合适的bazel版本进行下载。
如果CUDA和Tensorflow的版本与本文一致,也可点此链接下载:bazel-0.20.0-windows-x86_64.exe
github上选择下载文件如下图所示:

在这里插入图片描述
将下载好的 .exe 文件复制到 D:\msys64 下(路径根据个人安装情况而定),更名为 bazel.exe

在这里插入图片描述
复制完成后,配置bazel环境变量。
新建三个系统变量:BAZEL_SHBAZEL_VCBAZEL_VS。相应的路径如以下表格所示:

变量
BAZEL_SHD:\msys64\usr\bin\bash.exe
BAZEL_VCD:\Program Files (x86)\Microsoft Visual Studio 14.0\VC
BAZEL_VSD:\Program Files (x86)\Microsoft Visual Studio 14.0

环境变量配置完成:

在这里插入图片描述

bazel安装配置完毕!

Step4. 下载编译所需文件(tensorflow源码及其他文件)

进入tensorflow-v1.13.1源码的github的页面 ,点击页面右侧绿色按钮Clone or download,然后点击Download ZIP进行下载,如下图所示。
或者也可直接点此链接:tensorflow-1.13.1 进行下载。
下载时间也许会很长,请耐心等待。
在这里插入图片描述
进入tensorflow-windows-build-script-master的github页面,下载tensorflow-windows-build-script-master.zip(此链接可直接下载)。

在这里插入图片描述

以上两个 .zip 文件下载完成后,在D盘新建一个文件夹,命名为 tensorflow-1.13.1 (也可根据个人喜好决定)。

  • 将下载好的 tensorflow-1.13.1.zip 解压到刚刚新建的文件夹下,重新命名为 source
  • tensorflow-windows-build-script-master.zip 解压到任意位置,然后把其中的 patchesbuild.ps1 文件,复制到新建的 D:\tensorflow-1.13.1目录下,如下图所示:

在这里插入图片描述

  • patches 下的 eigen_half.patch 复制到 tensorflow-1.13.1\source\third_party 下:

在这里插入图片描述

  • patches 下的 tf_exported_symbols_msvc.lds 复制到 tensorflow-1.13.1\source\tensorflow 下:

在这里插入图片描述

  • 用文本编辑器打开 build.ps1 文件,将以下语句注释掉:

Copy-Item …\patches\tf_exported_symbols_msvc.lds tensorflow\

防止编译时出现Copy-Item命令的问题。
位置在 build.ps1 的180行,如下图所示:

在这里插入图片描述
编译所需文件准备完毕!

Step5. 使用powershell进行配置与编译

C:\Windows\SysWOW64\WindowsPowerShell\v1.0 目录下,右键以管理员身份运行 powershell.exe

在这里插入图片描述
powershell 窗口中输入以下命令,转到 tensorflow-1.13.1 目录下:

cd D:\tensorflow-1.13.1

输入bazel编译的选项:

$parameterString = "--config=opt --config=cuda --define=no_tensorflow_py_deps=true --copt=-nvcc_options=disable-warnings //tensorflow:libtensorflow_cc.so --verbose_failures"

然后输入以下命令,执行 build.ps1 脚本文件:

.\build.ps1 -BazelBuildParameters $parameterString -BuildCppAPI -ReserveSource

执行命令时出现 UnauthorizedAccess 错误,说明可能是 powershell 的执行策略受限,输入以下命令查看当前执行策略:

Get-ExecutionPolicy

我这里显示的是Restricted(受限的),所以需要输入以下语句来取消限制:

Set-ExecutionPolicy Unrestricted

询问是否改变执行策略,输入 y,回车。
修改好后,可以再次输入:

Get-ExecutionPolicy

查看当前执行策略是否已经取消限制。

在这里插入图片描述
执行策略的问题解决以后,重新执行 build.ps1 脚本文件:

.\build.ps1 -BazelBuildParameters $parameterString -BuildCppAPI -ReserveSource

如果一切正常,我们将开始编译前的配置。可参照以下两图进行配置。有些提问可以直接按回车来选择默认配置,括号中出现 default 字眼的,都可以这么做。

在这里插入图片描述
注意 :当问到GPU的计算能力(compute capability),即出现 [Default is: 3.5, 7.0]: 时,先不要急着按回车。我们先找到跟自己显卡对应的计算能力,再进行填写,可参考后面的方法来查看显卡的计算能力。

在这里插入图片描述
打开设备管理器,找到自己的显卡型号:

在这里插入图片描述
然后进入NVDIA官网的CUDA GPUs页面,点击 CUDA-Enabled GeForce and TITAN Products,找到自己的显卡型号对应的数值,将其填入 [Default is: 3.5, 7.0]: 后面,回车继续配置。

在这里插入图片描述
都配置完成后,编译正式开始。注意保持网络通畅,因为编译之前需要下载各种依赖库,网络异常会导致下载失败停止编译。编译时间很长,不要光盯着屏幕看,该吃吃该喝喝该玩玩该睡睡。

编译可能出现无法解析的外部符号的问题,以下面的错误为例:

无法解析的外部符号 “public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)” (??1LogMessage@internal@tensorflow@@UEAA@XZ),该符号在函数 “public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor’(void)” (??_DLogMessage@internal@tensorflow@@QEAAXXZ) 中被引用

解决方法:
用文本编辑器打开 D:\tensorflow-1.13.1\source\tensorflow\tf_exported_symbols_msvc.lds ,找到前面一个括号中带问号的内容:

??1LogMessage@internal@tensorflow@@UEAA@XZ

将其删除。如果有多个无法解析外部符号的问题,用同样的方法逐一删除即可。
确认全部清除后,重新执行以下命令配置和编译:

.\build.ps1 -BazelBuildParameters $parameterString -BuildCppAPI -ReserveSource

如果一切正常,在漫长的等待之后,将会出现以下结果:
注意:第一个红框部分为编译生成的动态库 libtensorflow_cc.so 所在位置,请记录下来,后面需要用到。

在这里插入图片描述
编译任务圆满完成!!!
可以稍作休息小小庆祝一下!

Step6. 整理生成的文件——dll,lib,include

新建一个文件夹,在文件夹中创建以下三个目录:dlllibinclude

在这里插入图片描述
根据编译结束时显示的动态库所在位置,找到生成的库 libtensorflow_cc.soliblibtensorflow_cc.so.ifso

  • libtensorflow_cc.so 更名为 tensorflow_cc.dll,放到刚才创建的 dll 目录下;
  • liblibtensorflow_cc.so.ifso 更名为 tensorflow_cc.lib,放到刚才创建的 lib 目录下。

在这里插入图片描述
接下来要做的是填满 include 目录,该步相对繁琐,最终将包含以下文件夹:

在这里插入图片描述

1)在include目录中新建名为 _bin 的文件夹。参考以下路径,打开 _embedded_binaries 目录,将下图红框中的文件复制到 _bin 文件夹中:

C:\Users\xxx_bazel_xxx\install\d5b1be53d8db6a1e2d160364df2e7ef6_embedded_binaries

在这里插入图片描述
2-1) 参考以下路径,将下图红框中的 bazel-out 文件夹复制到 include 目录下:

C:\Users\xxx_bazel_xxx\y46qiod6\execroot\org_tensorflow

在这里插入图片描述
2-2) 参考以下路径,将 protobuf_archive 文件夹复制到 include 下的 bazel-out\x64_windows-opt\genfiles\external 目录中:

C:\Users\xxx_bazel_xxx\y46qiod6\external

在这里插入图片描述

3) 将 D:\tensorflow-1.13.1\source 下的 tensorflowthird_party 复制到 include 目录下:

在这里插入图片描述
4-1) 参照以下路径,将下图红框中的 external 文件夹复制到 include 目录下:

C:\Users\xxx_bazel_xxx\y46qiod6

在这里插入图片描述
4-2) 参照以下路径,将 embedded_tools 文件夹下的所有文件复制到 include 下的 external\bazel_tools 目录中(如果 external 里面没有该文件夹,需新建并命名为 bazel_tools):

C:\Users\xxx_bazel_xxx\install\d5b1be53d8db6a1e2d160364df2e7ef6_embedded_binaries\embedded_tools

在这里插入图片描述

4-3) 将 D:\tensorflow-1.13.1\source 下的 tensorflow 复制到 include 下的 external\org_tensorflow 目录中(如果 external 里面没有该文件夹,需新建并命名为 rg_tensorflow):

在这里插入图片描述
至此,调用tensorflow所需的库及包含的目录准备完毕!

Step7. 调用模型测试程序

新建测试工程,在 .cpp 文件中写入以下代码:

#define COMPILER_MSVC
#define NOMINMAX
#define PLATFORM_WINDOWS   // 指定使用tensorflow/core/platform/windows/cpu_info.h

#include "stdafx.h"
#include<iostream>
#include<opencv2/opencv.hpp>
#include"tensorflow/core/public/session.h"
#include "tensorflow/core/platform/env.h"
#include <time.h>

using namespace tensorflow;
using namespace cv;
using std::cout;
using std::endl;

int main() {
	const std::string model_path = "frozen_inference_graph.pb";// tensorflow模型文件,注意不能含有中文
	const std::string image_path = "image1.jpg";    // 待inference的图片grace_hopper.jpg

	// 设置输入图像
	cv::Mat img = cv::imread(image_path);
	cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
	int height = img.rows;
	int width = img.cols;
	int depth = img.channels();

	// 取图像数据,赋给tensorflow支持的Tensor变量中
	tensorflow::Tensor input_tensor(DT_UINT8, TensorShape({ 1, height, width, depth }));
	const uint8* source_data = img.data;
	auto input_tensor_mapped = input_tensor.tensor<uint8, 4>();

	for (int i = 0; i < height; i++) {
		const uint8* source_row = source_data + (i * width * depth);
		for (int j = 0; j < width; j++) {
			const uint8* source_pixel = source_row + (j * depth);
			for (int c = 0; c < depth; c++) {
				const uint8* source_value = source_pixel + c;
				input_tensor_mapped(0, i, j, c) = *source_value;
			}
		}
	}

	// 初始化tensorflow session
	Session* session;
	Status status = NewSession(SessionOptions(), &session);
	if (!status.ok()) {
		std::cerr << status.ToString() << endl;
		return -1;
	}
	else {
		cout << "Session created successfully" << endl;
	}

	// 读取二进制的模型文件到graph中
	tensorflow::GraphDef graph_def;
	status = ReadBinaryProto(Env::Default(), model_path, &graph_def);
	if (!status.ok()) {
		std::cerr << status.ToString() << endl;
		return -1;
	}
	else {
		cout << "Load graph protobuf successfully" << endl;
	}

	// 将graph加载到session
	status = session->Create(graph_def);
	if (!status.ok()) {
		std::cerr << status.ToString() << endl;
		return -1;
	}
	else {
		cout << "Add graph to session successfully" << endl;
	}
	// 输入inputs,“ x_input”是我在模型中定义的输入数据名称
	std::vector<std::pair<std::string, tensorflow::Tensor>> inputs = {
		{ "image_tensor:0", input_tensor },
	};

	// 输出outputs
	std::vector<tensorflow::Tensor> outputs;

	//批处理识别
	double start = clock();
	std::vector<std::string> output_nodes;
	output_nodes.push_back("num_detections");
	output_nodes.push_back("detection_boxes");
	output_nodes.push_back("detection_scores");
	output_nodes.push_back("detection_classes");
	// 运行会话,最终结果保存在outputs中
	status = session->Run(inputs, { output_nodes }, {}, &outputs);
	if (!status.ok()) {
		std::cerr << status.ToString() << endl;
		return -1;
	}
	else {
		cout << "Run session successfully" << endl;
	}

	double	finish = clock();
	double duration = (double)(finish - start) / CLOCKS_PER_SEC;
	cout << "spend time:" << duration << endl;
	cv::imshow("image", img);
	cv::waitKey();
	return 0;
}

先别急着编译工程,首先我们需要把环境配置好:

  • 配置opencv (Release版本);
  • 配置tensorflow C++库。

opencv库配置就不多赘述,这里主要说明tensorflow库的配置。
1)包含目录:

D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\org_tensorflow;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\protobuf_archive\src;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\com_google_absl;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\external\eigen_archive;D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\include\bazel-out\x64_windows-opt\genfiles;

在这里插入图片描述

2)库目录:

D:\libtensorflow-gpu-windows-x86_64-1.13.1-avx2cuda10cudnn76\lib;

3)链接器输入附加依赖项:

tensorflow_cc.lib;

环境配置好后,将之前生成的动态库放入应用程序目录,即复制 tensorflow_cc.dll 到与工程文件 .sln 同级的 x64\Release 文件夹下,如下图所示:

在这里插入图片描述
接下来,从网盘下载模型文件 frozen_inference_graph.pb 和测试图片 image1.jpg(点此链接)

  • 模型文件在 tensorflow\ssd_mobilenet_v1_coco_2017_11_17 目录下;
  • 测试图片在 tensorflow\test_images 目录下。

把它们放到与 .cpp 文件同级的目录下,如下图所示:

在这里插入图片描述
编译前的准备工作完成,现在我们可以编译生成项目。生成之前,注意选择 Releasex64,如下图红框所示:

在这里插入图片描述
编译可能出现无法解析的外部符号的问题,以下面的错误为例:

无法解析的外部符号 “public: virtual __cdecl tensorflow::internal::LogMessage::~LogMessage(void)” (??1LogMessage@internal@tensorflow@@UEAA@XZ),该符号在函数 “public: void __cdecl tensorflow::internal::LogMessage::`vbase destructor’(void)” (??_DLogMessage@internal@tensorflow@@QEAAXXZ) 中被引用

解决方法:
用文本编辑器打开 D:\tensorflow-1.13.1\source\tensorflow\tf_exported_symbols_msvc.lds ,将前面一个括号中带问号的内容:

??1LogMessage@internal@tensorflow@@UEAA@XZ

复制到该文件的末尾,如下图所示。
如果有多个无法解析外部符号的问题,用同样的方法逐一添加。全部添加完成后保存关闭文件,然后重新编译tensorflow,也就是说从运行脚本文件开始,后面的流程要重新走一遍,想想有点心累,不过都已经走到这里了,半途而废有点说不过去呀,咬咬牙重来一遍吧。

在这里插入图片描述

上面的问题已经成功解决,没有意外,还会出现max问题:

在这里插入图片描述
解决办法如下:
双击错误提示,跳转到有max问题的文件: logging.htensor_shape.h,分别进行以下修改:

1)logging.h:

将第250行的

if (TF_PREDICT_FALSE(v2 >= std::numeric_limits<int>::max())) {      \

改为

if (TF_PREDICT_FALSE(v2 >= (std::numeric_limits<int>::max)())) {      \

修改后如下图所示:

在这里插入图片描述

2)tensor_shape.h:
将108-111行的

static const int64 kMaxRep16 = std::numeric_limits<uint16>::max() - 1;
static const int64 kMaxRep32 = std::numeric_limits<uint32>::max() - 1;
static const uint16 kUnknownRep16 = std::numeric_limits<uint16>::max();
static const uint32 kUnknownRep32 = std::numeric_limits<uint32>::max();

改为:

static const int64 kMaxRep16 = (std::numeric_limits<uint16>::max)() - 1;
static const int64 kMaxRep32 = (std::numeric_limits<uint32>::max)() - 1;
static const uint16 kUnknownRep16 = (std::numeric_limits<uint16>::max)();
static const uint32 kUnknownRep32 = (std::numeric_limits<uint32>::max)();

修改后如下图所示:

在这里插入图片描述
修改好后我们重新生成项目,如果上面的步骤都没有问题,将会出现下面的结果:

在这里插入图片描述
没错!编译成功!小腿抖起来小曲儿唱起来!

到这里还没有结束,将程序运行起来,你将看到以下结果:

在这里插入图片描述
红框部分说明GPU和模型调用没毛病,妥妥地成功!小腿再次抖起来小曲儿再次唱起来!

tensorflow C++库的生成和调用全部结束!

支线任务

注意,这是作业,不是彩蛋。
测试程序结尾出现了以下神秘图片:

在这里插入图片描述
蓝狗?!
还别说,狗狗蓝色的美瞳还真是洋气,蓝色的皮鞋更显骚气。
灵魂三问:小狗为什么是蓝色?定位框在哪?识别的结果在哪?
这就需要骚年们好好的研究和琢磨了。

Happy Ending:

在这里插入图片描述

<think>好的,我现在需要解决用户的问题:如何卸载或删除TensorFlow Federated(tensorflow-federated)这个Python包。用户可能已经安装了它,但现在想要移除,可能因为需要清理环境、解决依赖冲突,或者不再使用这个了。 首先,我应该回忆Python中卸载包的一般方法。通常,使用pip可以安装和卸载包。pip uninstall命令是最直接的,比如`pip uninstall package-name`。但用户可能已经用不同的方式安装了tensorflow-federated,比如通过源码安装或者使用特定的版本,这时候可能需要额外的步骤。 接下来,我需要考虑TensorFlow Federated(TFF)的特殊性。根据我的了解,TFF可能依赖于特定版本TensorFlow和其他,用户可能在安装时使用了特定的源,比如从GitHub安装,或者通过PyPI安装。这时候,如果直接用pip uninstall可能有效,但如果安装方式不同,可能需要其他方法。 然后,我需要验证标准的卸载步骤。首先,建议用户运行`pip uninstall tensorflow-federated`。这会触发pip的卸载流程,删除相关的文件和包。但有时候,如果包被其他包依赖,或者安装时有权限问题,可能会遇到错误。这时候可能需要加上`--yes`选项自动确认,或者使用sudo(在Linux/macOS上)来提升权限,不过要注意虚拟环境是否需要。 如果用户是通过源码安装的,比如从GitHub克隆仓并用`pip install .`安装,那么pip uninstall可能仍然有效,但可能需要确保卸载时的环境与安装时一致。这时候可能需要重新进入那个环境,或者手动删除相关文件,但手动删除风险较高,不推荐。 另外,用户可能使用了虚拟环境。如果是的话,卸载时确保激活了正确的环境。例如,在conda环境中,如果使用conda安装的,可能需要用conda uninstall,但通常pip安装的包也可以用pip卸载。但需要注意,有时混用conda和pip可能导致环境问题,所以最好保持一致性。 还有一种情况是,用户可能同时安装了多个版本的Python,导致pip指向错误的Python环境。这时候需要确认用户使用的pip是否正确,可以通过`pip --version`查看路径,或者使用`python -m pip uninstall tensorflow-federated`来明确指定Python版本。 此外,可能需要检查依赖项。卸载TFF后,相关的依赖包可能仍然存在,但通常pip不会自动移除这些依赖。如果用户想彻底清理,可能需要手动检查并移除不再需要的包,但需要谨慎操作,以免影响其他项目。 用户的问题中还提到参考引用[1]是关于Python的格式字符串,这可能与当前问题无关,可能用户误引用了,或者有其他上下文。但根据问题本身,可以忽略这个引用,专注于卸载步骤。 最后,应该提醒用户在卸载后验证是否成功,可以尝试导入tensorflow_federated,如果出现ModuleNotFoundError,说明卸载成功。否则,可能需要进一步排查。 总结步骤应该是: 1. 使用pip卸载:`pip uninstall tensorflow-federated` 2. 处理可能的权限问题,如使用sudo或在虚拟环境中操作 3. 验证卸载结果 4. 处理特殊情况,如源码安装或依赖问题 同时,用户可能想知道如何彻底清除残留文件,或者如何处理卸载后的依赖项,因此相关问题可以涉及这些方面。</think>要卸载或删除Python中的`tensorflow-federated`,可以按照以下步骤操作: --- ### **步骤1:使用pip卸载** 在终端或命令行中执行以下命令: ```bash pip uninstall tensorflow-federated ``` 系统会提示确认卸载,输入 `y` 并按回车键完成操作。 如果希望跳过确认步骤(例如在脚本中自动执行),可以添加 `--yes` 参数: ```bash pip uninstall tensorflow-federated --yes ``` --- ### **步骤2:处理特殊情况** - **虚拟环境**:如果安装时使用了虚拟环境(如`venv`或`conda`),需先激活对应环境再执行卸载命令。 ```bash # 对于conda环境 conda activate your_env_name pip uninstall tensorflow-federated ``` - **权限问题**:如果安装时使用了管理员权限,卸载时可能需要添加 `sudo`(仅限Linux/macOS): ```bash sudo pip uninstall tensorflow-federated ``` - **多Python版本**:若系统有多个Python版本(如Python 3.8和3.9),需明确指定版本: ```bash python3.9 -m pip uninstall tensorflow-federated ``` --- ### **步骤3:验证卸载** 尝试导入以确认是否成功卸载: ```python import tensorflow_federated ``` 如果出现 `ModuleNotFoundError`,则表明已成功卸载。 --- ### **补充说明** - **源码安装的卸载**:如果通过源码安装(例如从GitHub克隆后运行 `pip install .`),仍需使用 `pip uninstall tensorflow-federated` 完成卸载。 - **依赖项清理**:`tensorflow-federated`的依赖包(如TensorFlow)不会被自动移除。如需清理,需手动卸载相关包。 ---
评论 128
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值