给 Python 算法插上性能的翅膀——pybind11 落地实践

给 Python 算法插上性能的翅膀——pybind11 落地实践

转自:https://zhuanlan.zhihu.com/p/444805518

作者:jesonxiang(向乾彪),腾讯 TEG 后台开发工程师

1. 背景

目前 AI 算法开发特别是训练基本都以 Python 为主,主流的 AI 计算框架如 TensorFlow、PyTorch 等都提供了丰富的 Python 接口。有句话说得好,人生苦短,我用 Python。但由于 Python 属于动态语言,解释执行并缺少成熟的 JIT 方案,计算密集型场景多核并发受限等原因,很难直接满足较高性能要求的实时 Serving 需求。在一些对性能要求高的场景下,还是需要使用 C/C++来解决。但是如果要求算法同学全部使用 C++来开发线上推理服务,成本又非常高,导致开发效率和资源浪费。因此,如果有轻便的方法能将 Python 和部分 C++编写的核心代码结合起来,就能达到既保证开发效率又保证服务性能的效果。本文主要介绍 pybind11 在腾讯广告多媒体 AI Python 算法的加速实践,以及过程中的一些经验总结。

2. 业内方案

2.1 原生方案

Python 官方提供了 Python/C API,可以实现「用 C 语言编写 Python 库」,先上一段代码感受一下:

static PyObject *
spam_system(PyObject *self, PyObject *args)
{const char *command;int sts;if (!PyArg_ParseTuple(args, "s", &command))return NULL;sts = system(command);return PyLong_FromLong(sts);
}

可见改造成本非常高,所有的基本类型都必须手动改为 CPython 解释器封装的 binding 类型。由此不难理解,为何 Python 官网也建议大家使用第三方解决方案[1]。

2.2 Cython

Cython 主要打通的是 Python 和 C,方便为 Python 编写 C 扩展。Cython 的编译器支持转化 Python 代码为 C 代码,这些 C 代码可以调用 Python/C 的 API。从本质上来说,Cython 就是包含 C 数据类型的 Python。目前 Python 的 numpy,以及我厂的 tRPC-Python 框架有所应用。

缺点:

  • 需要手动植入 Cython 自带语法(cdef 等),移植和复用成本高
  • 需要增加其他文件,如 setup.py、*.pyx 来让你的 Python 代码最后能够转成性能较高的 C 代码
  • 对于 C++的支持程度存疑

2.3 SIWG

SIWG 主要解决其他高级语言与 C 和 C++语言交互的问题,支持十几种编程语言,包括常见的 java、C#、javascript、Python 等。使用时需要用*.i 文件定义接口,然后用工具生成跨语言交互代码。但由于支持的语言众多,因此在 Python 端性能表现不是太好。

值得一提的是,TensorFlow 早期也是使用 SWIG 来封装 Python 接口,正式由于 SIWG 存在性能不够好、构建复杂、绑定代码晦涩难读等问题,TensorFlow 已于 2019 年将 SIWG 切换为 pybind11[2]。

2.4 Boost.Python

C++中广泛应用的 Boost 开源库,也提供了 Python binding 功能。使用上,通过宏定义和元编程来简化 Python 的 API 调用。但最大的缺点是需要依赖庞大的 Boost 库,编译和依赖关系包袱重,只用于解决 Python binding 的话有一种高射炮打蚊子的既视感。

2.5 pybind11

可以理解为以 Boost.Python 为蓝本,仅提供 Python & C++ binding 功能的精简版,相对于 Boost.Python 在 binary size 以及编译速度上有不少优势。对 C++支持非常好,基于 C++11 应用了各种新特性,也许 pybind11 的后缀 11 就是出于这个原因。

Pybind11 通过 C++ 编译时的自省来推断类型信息,来最大程度地减少传统拓展 Python 模块时繁杂的样板代码, 且实现了常见数据类型,如 STL 数据结构、智能指针、类、函数重载、实例方法等到 Python 的自动转换,其中函数可以接收和返回自定义数据类型的值、指针或引用。

特点:

  • 轻量且功能单一,聚焦于提供 C++ & Python binding,交互代码简洁
  • 对常见的 C++数据类型如 STL、Python 库如 numpy 等兼容很好,无人工转换成本
  • only header 方式,无需额外代码生成,编译期即可完成绑定关系建立,减小 binary 大小
  • 支持 C++新特性,对 C++的重载、继承,debug 方式便捷易用
  • 完善的官方文档支持,应用于多个知名开源项目

“Talk is cheap, show me your code.” 三行代码即可快速实现绑定,你值得拥有:

PYBIND11_MODULE (libcppex, m) {m.def("add", [](int a, int b) -> int { return a + b; });
}

3. Python 调 C++

3.1 从 GIL 锁说起

GIL(Global Interpreter Lock)全局解释器锁:同一时刻在一个进程只允许一个线程使用解释器,导致多线程无法真正用到多核。由于持有锁的线程在执行到 I/O 密集函数等一些等待操作时会自动释放 GIL 锁,所以对于 I/O 密集型服务来说,多线程是有效果的。但对于 CPU 密集型操作,由于每次只能有一个线程真正执行计算,对性能的影响可想而知。

在这里插入图片描述

这里必须说明的是,GIL 并不是 Python 本身的缺陷,而是目前 Python 默认使用的 CPython 解析器引入的线程安全保护锁。我们一般说 Python 存在 GIL 锁,其实只针对于 CPython 解释器。那么如果我们能想办法避开 GIL 锁,是不是就能有很不错的加速效果?答案是肯定的,一种方案是改为使用其他解释器如 pypy 等,但对于成熟的 C 扩展库兼容不够好,维护成本高。另一种方案,就是通过 C/C++扩展来封装计算密集部分代码,并在执行时移除 GIL 锁。

3.2 Python 算法性能优化

pybind11 就提供了在 C++端手动释放 GIL 锁的接口,因此,我们只需要将密集计算的部分代码,改造成 C++代码,并在执行前后分别释放/获取 GIL 锁,Python 算法的多核计算能力就被解锁了。当然,除了显示调用接口释放 GIL 锁的方法之外,也可以在 C++内部将计算密集型代码切换到其他 C++线程异步执行,也同样可以规避 GIL 锁利用多核。

下面以 100 万次城市间球面距离计算为例,对比 C++扩展前后性能差异:

C++端:

#include <math.h>
#include <stdio.h>
#include <time.h>
#include <pybind11/embed.h>namespace py = pybind11;const double pi = 3.1415926535897932384626433832795;double rad(double d) {return d * pi / 180.0;
}double geo_distance(double lon1, double lat1, double lon2, double lat2, int test_cnt) {py::gil_scoped_release release;     // 释放GIL锁double a, b, s;double distance = 0;for (int i = 0; i < test_cnt; i++) {double radLat1 = rad(lat1);double radLat2 = rad(lat2);a = radLat1 - radLat2;b = rad(lon1) - rad(lon2);s = pow(sin(a/2),2) + cos(radLat1) * cos(radLat2) * pow(sin(b/2),2);distance = 2 * asin(sqrt(s)) * 6378 * 1000;}py::gil_scoped_acquire acquire;     // C++执行结束前恢复GIL锁return distance;
}PYBIND11_MODULE (libcppex, m) {m.def("geo_distance", &geo_distance, R"pbdoc(Compute geography distance between two places.)pbdoc");
}

Python 调用端:

import sys
import time
import math
import threadingfrom libcppex import *def rad(d):return d * 3.1415926535897932384626433832795 / 180.0def geo_distance_py(lon1, lat1, lon2, lat2, test_cnt):distance = 0for i in range(test_cnt):radLat1 = rad(lat1)radLat2 = rad(lat2)a = radLat1 - radLat2b = rad(lon1) - rad(lon2)s = math.sin(a/2)**2 + math.cos(radLat1) * math.cos(radLat2) * math.sin(b/2)**2distance = 2 * math.asin(math.sqrt(s)) * 6378 * 1000print(distance)return distancedef call_cpp_extension(lon1, lat1, lon2, lat2, test_cnt):res = geo_distance(lon1, lat1, lon2, lat2, test_cnt)print(res)return resif __name__ == "__main__":threads = []test_cnt = 1000000test_type = sys.argv[1]thread_cnt = int(sys.argv[2])start_time = time.time()for i in range(thread_cnt):if test_type == 'p':t = threading.Thread(target=geo_distance_py,args=(113.973129, 22.599578, 114.3311032, 22.6986848, test_cnt,))elif test_type == 'c':t = threading.Thread(target=call_cpp_extension,args=(113.973129, 22.599578, 114.3311032, 22.6986848, test_cnt,))threads.append(t)t.start()for thread in threads:thread.join()print('calc time = %d' % int((time.time() - start_time) * 1000))

性能对比

  • 单线程时耗:Python 964ms,C++ 7ms

    $ python test.py p 1
    38394.662146601186
    calc time = 964
    $ python test.py c 1
    38394.662146601186
    calc time = 7
    
  • 10 线程时耗:Python 18681ms,C++ 13ms

    $ python test.py p 10
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    calc time = 18681
    $ python test.py c 10
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    38394.662146601186
    calc time = 13
    

CPU利用率

  • Python 多线程无法同时刻多核并行计算,仅相当于单核利用率

  • C++可以吃满 DevCloud 机器的 10 个 CPU 核

    pythoncpp
    在这里插入图片描述在这里插入图片描述

结论:

计算密集型代码,单纯改为 C++实现即可获得不错的性能提升,在多线程释放 GIL 锁的加持下,充分利用多核,性能轻松获得线性加速比,大幅提升资源利用率。虽然实际场景中也可以用 Python 多进程的方式来利用多核,但是在模型越来越大动辄数十 G 的趋势下,内存占用过大不说,进程间频繁切换的 context switching overhead,以及语言本身的性能差异,导致与 C++扩展方式依然有不少差距。

注:以上测试 demo github 地址:https://github.com/jesonxiang/cpp_extension_pybind11,测试环境为 CPU 10 核容器,大家有兴趣也可以做性能验证。

3.3 编译环境

编译指令:

g++ -Wall -shared -std=gnu++11 -O2 -fvisibility=hidden -fPIC -I./ perfermance.cc -o libcppex.so `python3-config --cflags --ldflags --libs`

如果 Python 环境未正确配置可能报错:

在这里插入图片描述

这里对 Python 的依赖是通过 python3-config --cflags --ldflags --libs 来自动指定,可先单独运行此命令来验证 Python 依赖是否配置正确。python3-config 正常执行依赖 Python3-dev,可以通过以下命令安装:

apt install python3-dev

4. C++调 Python

一般 pybind11 都是用于给 C++代码封装 Python 端接口,但是反过来 C++调 Python 也是支持的。只需#include <pybind11/embed.h>头文件即可使用,内部是通过嵌入 CPython 解释器来实现。使用上也非常简单易用,同时有不错的可读性,与直接调用 Python 接口非常类似。比如对一个 numpy 数组调用一些方法,参考示例如下:

// C++
pyVec = pyVec.attr("transpose")().attr("reshape")(pyVec.size());# Python
pyVec = pyVec.transpose().reshape(pyVec.size)

以下以我们开发的 C++ GPU 高性能版抽帧 so 为例,除了提供抽帧接口给到 Python 端调用,还需要回调给 Python 从而通知抽帧进度以及帧数据。

Python 端回调接口:

def on_decoding_callback(task_id:str, progress:int):print("decoding callback, task id: %s, progress: %d" % (task_id, progress))if __name__ == "__main__":decoder = DecoderWrapper()decoder.register_py_callback(os.getcwd() + "/decode_test.py","on_decoding_callback")

C++端接口注册 & 回调 Python:

#include <pybind11/embed.h>int DecoderWrapper::register_py_callback(const std::string &py_path,const std::string &func_name) {int ret = 0;const std::string &pyPath = py_get_module_path(py_path);const std::string &pyName = py_get_module_name(py_path);SoInfo("get py module name: %s, path: %s", pyName.c_str(), pyPath.c_str());py::gil_scoped_acquire acquire;py::object sys = py::module::import("sys");sys.attr("path").attr("append")(py::str(pyPath.c_str())); //Python脚本所在的路径py::module pyModule = py::module::import(pyName.c_str());if (pyModule == NULL) {LogError("Failed to load pyModule ..");py::gil_scoped_release release;return PYTHON_FILE_NOT_FOUND_ERROR;}if (py::hasattr(pyModule, func_name.c_str())) {py_callback = pyModule.attr(func_name.c_str());} else {ret = PYTHON_FUNC_NOT_FOUND_ERROR;}py::gil_scoped_release release;return ret;}int DecoderListener::on_decoding_progress(std::string &task_id, int progress) {if (py_callback != NULL) {try {py::gil_scoped_acquire acquire;py_callback(task_id, progress);py::gil_scoped_release release;} catch (py::error_already_set const &PythonErr) {LogError("catched Python exception: %s", PythonErr.what());} catch (const std::exception &e)  {LogError("catched exception: %s", e.what());} catch (...) {LogError("catched unknown exception");}}
}

5. 数据类型转换

5.1 类成员函数

对于类和成员函数的 binding,首先需要构造对象,所以分为两步:第一步是包装实例构造方法,另一步是注册成员函数的访问方式。同时,也支持通过 def_static、def_readwrite 来绑定静态方法或成员变量,具体可参考官方文档[3]。

#include <pybind11/pybind11.h>class Hello
{
public:Hello(){}void say( const std::string s ){std::cout << s << std::endl;}
};PYBIND11_MODULE(py2cpp, m) {m.doc() = "pybind11 example";pybind11::class_<Hello>(m, "Hello").def(pybind11::init())  //构造器,对应c++类的构造函数,如果没有声明或者参数不对,会导致调用失败.def( "say", &Hello::say );
}/*
Python 调用方式:
c = py2cpp.Hello()
c.say()
*/

5.2 STL 容器

pybind11 支持 STL 容器自动转换,当需要处理 STL 容器时,只要额外包括头文件<pybind11/stl.h>即可。pybind11 提供的自动转换包括:std::vector<>/std::list<>/std::array<> 转换成 Python list ;std::set<>/std::unordered_set<> 转换成 Python set ; std::map<>/std::unordered_map<> 转换成 dict 等。此外 std::pair<> 和 std::tuple<>的转换也在 <pybind11/pybind11.h> 头文件中提供了。

#include <iostream>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>class ContainerTest {
public:ContainerTest() {}void Set(std::vector<int> v) {mv = v;}
private:std::vector<int> mv;
};PYBIND11_MODULE( py2cpp, m ) {m.doc() = "pybind11 example";pybind11::class_<ContainerTest>(m, "CTest").def( pybind11::init() ).def( "set", &ContainerTest::Set );
}/*
Python 调用方式:
c = py2cpp.CTest()
c.set([1,2,3])
*/

5.3 bytes、string 类型传递

由于在 Python3 中 string 类型默认为 UTF-8 编码,如果从 C++端传输 string 类型的 protobuf 数据到 Python,则会出现 “UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xba in position 0: invalid start byte” 的报错。

解决方案:pybind11 提供了非文本数据的 binding 类型 py::bytes:

m.def("return_bytes",[]() {std::string s("\xba\xd0\xba\xd0");  // Not valid UTF-8return py::bytes(s);  // Return the data without transcoding}
);

5.4 智能指针

  • std::unique_ptr pybind11 支持直接转换:
std::unique_ptr<Example> create_example() { return std::unique_ptr<Example>(new Example()); }
m.def("create_example", &create_example);
  • std::shared_ptr 需要特别注意的是,不能直接使用裸指针。如下的 get_child 函数在 Python 端调用会报内存访问异常(如 segmentation fault)。
class Child { };class Parent {
public:Parent() : child(std::make_shared<Child>()) { }Child *get_child() { return child.get(); }  /* Hint: ** DON'T DO THIS ** */
private:std::shared_ptr<Child> child;
};PYBIND11_MODULE(example, m) {py::class_<Child, std::shared_ptr<Child>>(m, "Child");py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent").def(py::init<>()).def("get_child", &Parent::get_child);
}

5.5 cv::Mat 到 numpy 转换

抽帧结果返回给 Python 端时,由于目前 pybind11 暂不支持自动转换 cv::Mat 数据结构,因此需要手动处理 C++ cv::Mat 和 Python 端 numpy 之间的绑定。转换代码如下:

/*Python->C++ Mat
*/
cv::Mat numpy_uint8_3c_to_cv_mat(py::array_t<uint8_t>& input) {if (input.ndim() != 3)throw std::runtime_error("3-channel image must be 3 dims ");py::buffer_info buf = input.request();cv::Mat mat(buf.shape[0], buf.shape[1], CV_8UC3, (uint8_t*)buf.ptr);return mat;
}/*C++ Mat ->numpy
*/
py::array_t<uint8_t> cv_mat_uint8_3c_to_numpy(cv::Mat& input) {py::array_t<uint8_t> dst = py::array_t<uint8_t>({ input.rows,input.cols,3}, input.data);return dst;
}

5.6 zero copy

一般来说跨语言调用都产生性能上的 overhead,特别是对于大数据块的传递。因此,pybind11 也支持了数据地址传递的方式,避免了大数据块在内存中的拷贝操作,性能上提升很大。

class Matrix {
public:Matrix(size_t rows, size_t cols) : m_rows(rows), m_cols(cols) {m_data = new float[rows*cols];}float *data() { return m_data; }size_t rows() const { return m_rows; }size_t cols() const { return m_cols; }
private:size_t m_rows, m_cols;float *m_data;
};py::class_<Matrix>(m, "Matrix", py::buffer_protocol()).def_buffer([](Matrix &m) -> py::buffer_info {return py::buffer_info(m.data(),                               /* Pointer to buffer */sizeof(float),                          /* Size of one scalar */py::format_descriptor<float>::format(), /* Python struct-style format descriptor */2,                                      /* Number of dimensions */{ m.rows(), m.cols() },                 /* Buffer dimensions */{ sizeof(float) * m.cols(),             /* Strides (in bytes) for each index */sizeof(float) });});

6. 落地 & 行业应用

上述方案,我们已在广告多媒体 AI 的色彩提取相关服务、GPU 高性能抽帧等算法中落地,取得了非常不错的提速效果。业内来说,目前市面上大部分 AI 计算框架,如 TensorFlow、Pytorch、阿里 X-Deep Learning、百度 PaddlePaddle 等,均使用 pybind11 来提供 C++到 Python 端接口封装,其稳定性以及性能均已得到广泛验证。

7. 结语

在 AI 领域普遍开源节流、降本提效的大背景下,如何充分利用好现有资源,提升资源利用率是关键。本文提供了一个非常便捷的提升 Python 算法服务性能,以及 CPU 利用率的解决方案,并在线上取得了不错的效果。

8. 附录

[1]https://docs.python.org/3/extending/index.html#extending-index

[2]https://github.com/tensorflow/community/blob/master/rfcs/20190208-pybind11.md#replace-swig-with-pybind11

[3]https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/532456.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chrome自动提交文件_收集文档及提交名单统计

知乎文章若有排版问题请见谅&#xff0c;原文放在个人博客中【欢迎互踩&#xff01;】文叔叔文档收集使用动机在我们的学习工作中&#xff0c;少不了要让大家集体提交文件的情况&#xff0c;举个最简单的例子&#xff1a;收作业。 传统的文件收集流程大致是&#xff1a;群内发出…

惠普800g1支持什么内存_惠普黑白激光打印机哪种好 惠普黑白激光打印机推荐【图文详解】...

打印机的出现让我们在生活和日常工作中变得越来越方便&#xff0c;不过随着科技的发展&#xff0c;打印机的类型也变得非常多&#xff0c;其中就有黑白激光打印机&#xff0c;而黑白激光打印机的品牌也有很多&#xff0c;比如我们的惠普黑白激光打印机&#xff0c;今天小编就给…

控制台输出颜色控制

控制台输出颜色控制 转自&#xff1a;https://cloud.tencent.com/developer/article/1142372 前端时间&#xff0c;写了一篇 PHP 在 Console 模式下的进度显示 &#xff0c;正好最近的一个数据合并项目需要用到控制台颜色输出&#xff0c;所以就把相关的信息整理下&#xff0c;…

idea连接跳板机_跳板机服务(jumpserver)

一、跳板机服务作用介绍1、有效管理用户权限信息2、有效记录用户登录情况3、有效记录用户操作行为二、跳板机服务架构原理三、跳板机服务安装过程第一步&#xff1a;安装跳板机依赖软件yum -y install git python-pip mariadb-devel gcc automake autoconf python-devel readl…

【详细图解】再次理解im2col

【详细图解】再次理解im2col 转自&#xff1a;https://mp.weixin.qq.com/s/GPDYKQlIOq6Su0Ta9ipzig 一句话&#xff1a;im2col是将一个[C,H,W]矩阵变成一个[H,W]矩阵的一个方法&#xff0c;其原理是利用了行列式进行等价转换。 为什么要做im2col? 减少调用gemm的次数。 重要…

反思 大班 快乐的机器人_幼儿园大班教案《快乐的桌椅》含反思

大班教案《快乐的桌椅》含反思适用于大班的体育主题教学活动当中&#xff0c;让幼儿提高协调性和灵敏性&#xff0c;创新桌椅的玩法&#xff0c;正确爬的方法&#xff0c;学会匍匐前进&#xff0c;快来看看幼儿园大班《快乐的桌椅》含反思教案吧。幼儿园大班教案《快乐的桌椅》…

DCN可形变卷积实现1:Python实现

DCN可形变卷积实现1&#xff1a;Python实现 我们会先用纯 Python 实现一个 Pytorch 版本的 DCN &#xff0c;然后实现其 C/CUDA 版本。 本文主要关注 DCN 可形变卷积的代码实现&#xff0c;不会过多的介绍其思想&#xff0c;如有兴趣&#xff0c;请参考论文原文&#xff1a; …

蓝牙耳机声音一顿一顿的_线控耳机党阵地转移成功,OPPO这款TWS耳机体验满分...

“你看到我手机里3.5mm的耳机孔了吗”&#xff0c;这可能是许多线控耳机党最想说的话了。确实&#xff0c;如今手机在做“减法”&#xff0c;而厂商们首先就拿3.5mm耳机孔“开刀”&#xff0c;我们也丧失了半夜边充电边戴耳机打游戏的乐趣。竟然如此&#xff0c;那如何在耳机、…

AI移动端优化之Im2Col+Pack+Sgemm

AI移动端优化之Im2ColPackSgemm 转自&#xff1a;https://blog.csdn.net/just_sort/article/details/108412760 这篇文章是基于NCNN的Sgemm卷积为大家介绍Im2ColPackSgemm的原理以及算法实现&#xff0c;希望对算法优化感兴趣或者做深度学习模型部署的读者带来帮助。 1. 前言 …

elementui的upload组件怎么获取上传的文本流、_抖音feed流直播间引流你还不会玩?实操讲解...

本文由艾奇在线明星优化师写作计划出品在这个全民惊恐多灾多难且带有魔幻的2020&#xff0c;一场突如其来的疫情改变了人们很多消费习惯&#xff0c;同时加速了直播电商的发展&#xff0c;现在直播已经成为商家必争的营销之地&#xff0c;直播虽然很火&#xff0c;但如果没有流…

FFmpeg 视频处理入门教程

FFmpeg 视频处理入门教程 转自&#xff1a;https://www.ruanyifeng.com/blog/2020/01/ffmpeg.html 作者&#xff1a; 阮一峰 日期&#xff1a; 2020年1月14日 FFmpeg 是视频处理最常用的开源软件。 它功能强大&#xff0c;用途广泛&#xff0c;大量用于视频网站和商业软件&…

checkbox wpf 改变框的大小_【论文阅读】倾斜目标范围框(标注)的终极方案

前言最常用的斜框标注方式是在正框的基础上加一个旋转角度θ&#xff0c;其代数表示为(x_c,y_c,w,h,θ)&#xff0c;其中(x_c,y_c )表示范围框中心点坐标&#xff0c;(w,h)表示范围框的宽和高[1,2,7]。对于该标注方式&#xff0c;如果将w和h的值互换&#xff0c;再将θ加上或者…

彻底理解BP之手写BP图像分类你也行

彻底理解BP之手写BP图像分类你也行 转自&#xff1a;https://zhuanlan.zhihu.com/p/397963213 第一节&#xff1a;用矩阵的视角&#xff0c;看懂BP的网络图 1.1、什么是BP反向传播算法 BP(Back Propagation)误差反向传播算法&#xff0c;使用反向传播算法的多层感知器又称为B…

梯度下降法和牛顿法计算开根号

梯度下降法和牛顿法计算开根号 本文将介绍如何不调包&#xff0c;只能使用加减乘除法实现对根号x的求解。主要介绍梯度下降和牛顿法者两种方法&#xff0c;并给出 C 实现。 梯度下降法 思路/步骤 转化问题&#xff0c;将 x\sqrt{x}x​ 的求解转化为最小化目标函数&#xff…

汇博工业机器人码垛机怎么写_全自动码垛机器人在企业生产中的地位越来越重要...

全自动码垛机器人在企业生产中的地位越来越重要在智能化的各种全自动生产线中&#xff0c;全自动码垛机器人成了全自动生产线的重要机械设备&#xff0c;在各种生产中发挥着不可忽视的作用。全自动码垛机器人主要用于生产线上的包装过程中&#xff0c;不仅能够提高企业的生产率…

小说中场景的功能_《流浪地球》:从小说到电影

2019年春节贺岁档冒出一匹黑马&#xff1a;国产科幻片《流浪地球》大年初一上映后口碑、票房双丰收&#xff1a;截至9日下午&#xff0c;票房已破15亿&#xff0c;并获得9.2的高评分。著名导演詹姆斯卡梅隆通过社交媒体对我国春节期间上映的科幻影片《流浪地球》发出的祝愿&…

线性回归与逻辑回归及其实现

线性回归与逻辑回归及其实现 回归与分类 预测值定性分析&#xff0c;即离散变量预测时&#xff0c;称之为分类&#xff1b;预测值定量分析&#xff0c;即连续变量预测时&#xff0c;称之为回归。 如预测一张图片是猫还是狗&#xff0c;是分类问题&#xff1b;预测明年的房价…

hbase 页面访问_HBase

HBase 特点 海量存储 Hbase 适合存储 PB 级别的海量数据&#xff0c;在 PB 级别的数据以及采用廉价 PC 存储的情况下&#xff0c;能在几十到百毫秒内返回数据。这与 Hbase 的极易扩展性息息相关。正式因为 Hbase 良好的扩展性&#xff0c;才为海量数据的存储提供了便利。 2&…

深入理解L1、L2正则化

深入理解L1、L2正则化 转自&#xff1a;【面试看这篇就够了】L1、L2正则化理解 一、概述 正则化&#xff08;Regularization&#xff09;是机器学习中一种常用的技术&#xff0c;其主要目的是控制模型复杂度&#xff0c;减小过拟合。正则化技术已经成为模型训练中的常用技术&a…

机器学习中的概率模型

机器学习中的概率模型 转自&#xff1a;https://zhuanlan.zhihu.com/p/164551678 机器学习中的概率模型 概率论&#xff0c;包括它的延伸-信息论&#xff0c;以及随机过程&#xff0c;在机器学习中有重要的作用。它们被广泛用于建立预测函数&#xff0c;目标函数&#xff0c;以…