在Python中調用C/C++:cython及pybind11

在Python中調用C/C++:cython及pybind11

轉自:https://zhuanlan.zhihu.com/p/442935082

Python寫起來非常方便, 但面對大量for循環的時候, 執行速度有些捉急. 原因在于, python是一種動態類型語言, 在運行期間才去做數據類型檢查, 這樣效率就很低(尤其是大規模for循環的時候).

相比而言, C/C++每個變量的類型都是事先給定的, 通過編譯生成二進制可執行文件. 相比與python, C/C++效率比較高, 大規模for循環執行速度很快.

既然python的短板在于速度, 所以, 為了給python加速, 能否在Python中調用C/C++的代碼?

Python解釋器

當我們編寫Python代碼時,我們得到的是一個包含Python代碼的以.py為擴展名的文本文件。要運行代碼,就需要Python解釋器去執行.py文件。

(你給我翻譯翻譯, 什么叫python代碼)

Cython

當我們從Python官方網站下載并安裝好Python后,我們就直接獲得了一個官方版本的解釋器:CPython。這個解釋器是用C語言開發的,所以叫CPython。在命令行下運行python就是啟動CPython解釋器。CPython是使用最廣的Python解釋器。

雖然CPython效率低, 但是如果用它去調用C/C++代碼, 效果還是挺好的. 像numpy之類的數學運算庫, 很多都是用C/C++寫的. 這樣既能利用python簡潔的語法, 又能利用C/C++高效的執行速度. 有些情況下numpy效率比自己寫C/C++還高, 因為numpy利用了CPU指令集優化和多核并行計算.

我們今天要講的Python調用C/C++, 都是基于CPython解釋器的.

IronPython

IronPythonJython類似,只不過IronPython是運行在微軟.Net平臺上的Python解釋器,可以直接把Python代碼編譯成.Net的字節碼。缺點在于, 因為numpy等常用的庫都是用C/C++編譯的, 所以在IronPython中調用numpy等第三方庫非常不方便. (現在微軟已經放棄對IronPython的更新了)

Jython

Jython是運行在Java平臺上的Python解釋器,可以直接把Python代碼編譯成Java字節碼執行。Jython的好處在于能夠調用Java相關的庫, 壞處跟IronPython一樣.

PyPy

PyPy一個基于Python的解釋器,也就是用python解釋.py. 它的目標是執行速度。PyPy采用JIT技術,對Python代碼進行動態編譯(注意不是解釋),所以可以顯著提高Python代碼的執行速度。

為什么動態解釋慢

假設我們有一個簡單的python函數

 def add(x, y):return x + y

然后CPython執行起來大概是這個樣子(偽代碼)

if instance_has_method(x, '__add__') {// x.__add__ 里面又有一大堆針對不同類型的 y 的判斷return call(x, '__add__', y);
} else if isinstance_has_method(super_class(x), '__add__' {return call(super_class, '__add__', y);
} else if isinstance(x, str) and isinstance(y, str) {return concat_str(x, y);
} else if isinstance(x, float) and isinstance(y, float) {return add_float(x, y);
} else if isinstance(x, int) and isinstance(y, int) {return add_int(x, y);
} else ...

因為Python的動態類型, 一個簡單的函數, 要做很多次類型判斷. 這還沒完,你以為里面把兩個整數相加的函數,就是 C 語言里面的 x + y 么? No.

Python里萬物皆為對象, 實際上Python里的int大概是這樣一個結構體(偽代碼).

 struct {prev_gc_obj *objnext_gc_obj *objtype IntTypevalue IntValue... other fields}

每個 int 都是這樣的結構體,還是動態分配出來放在 heap 上的,里面的 value 還不能變,也就是說你算 1000 這個結構體加 1000 這個結構體,需要在heap里malloc出來 2000 這個結構體. 計算結果用完以后, 還要進行內存回收. (執行這么多操作, 速度肯定不行)

所以, 如果能夠靜態編譯執行+指定變量的類型, 將大幅提升執行速度.

Cython

什么是Cython

cython是一種新的編程語言, 它的語法基于python, 但是融入了一些C/C++的語法. 比如說, cython里可以指定變量類型, 或是使用一些C++里的stl庫(比如使用std::vector), 或是調用你自己寫的C/C++函數.

注意: Cython不是CPython!

原生Python

我們有一個RawPython.py

from math import sqrt
import timedef func(n):res = 0for i in range(1, n):res = res + 1.0 / sqrt(i)return resdef main():start = time.time()res = func(30000000)print(f"res = {res}, use time {time.time() - start:.5}")if __name__ == '__main__':main() 

我們先使用Python原生方式來執行看一下需要多少時間, 在我電腦上要花4秒。

編譯運行Cython程序

首先, 把一個cython程序轉化成.c/.cpp文件, 然后用C/C++編譯器, 編譯生成二進制文件. 在Windows下, 我們需要安裝Visual Studio/mingw等編譯工具. 在Linux或是Mac下, 我們需要安裝gcc, clang 等編譯工具.

  • 通過pip安裝cython
pip install cython

把 RawPython.py 重命名為 RawPython1.pyx

  • 編譯的話, 有兩種辦法:

(1)用setup.py編譯

增加一個setup.py, 添加以下內容. 這里language_level的意思是, 使用Python 3.

from distutils.core import setup
from Cython.Build import cythonizesetup(ext_modules = cythonize('RawPython1.pyx', language_level=3)
)

把Python編譯為二進制代碼

python setup.py build_ext --inplace

然后, 我們發現當前目錄下多了RawPython1.c(由.pyx轉化生成), 和RawPython1.pyd(由.c編譯生成的二進制文件).

(2)直接在命令行編譯(以gcc為例)

cython RawPython1.pyx
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python3.x -o RawPython1.so RawPython1.c

第一句是把.pyx轉化成.c, 第二句是用gcc編譯+鏈接.

  • 在當前目錄下, 運行
python -c "import RawPython1; RawPython1.main()"

我們可以導入編譯好的RawPython1模塊, 然后在Python中調用執行.

由以上的步驟的執行結果來看,并沒有提高太多,只大概提高了一倍的速度,這是因為Python的運行速度慢除了因為是解釋執行以外還有一個最重要的原因是Python是動態類型語言,每個變量在運行前是不知道類型是什么的,所以即便編譯為二進制代碼同樣速度不會太快,這時候我們需要深度使用Cython來給Python提速了,就是使用Cython來指定Python的數據類型。

加速! 加速!

指定變量類型

cython的好處是, 可以像C語言一樣, 顯式地給變量指定類型. 所以, 我們在cython的函數中, 加入循環變量的類型.

然后, 用C語言中的sqrt實現開方操作.

 def func(int n):cdef double res = 0cdef int i, num = nfor i in range(1, num):res = res + 1.0 / sqrt(i)return res

但是, python中math.sqrt方法, 返回值是一個Pythonfloat對象, 這樣效率還是比較低.

為了, 我們能否使用C語言的sqrt函數? 當然可以~

Cython對一些常用的C函數/C++類做了包裝, 可以直接在Cython里進行調用.

我們把開頭的

from math import sqrt

換成

from libc.math cimport sqrt

再按照上面的方式編譯運行, 發現速度提高了不少.

改造后的完整代碼如下:

import time
from libc.math cimport sqrt
def func(int n):cdef double res = 0cdef int i, num = nfor i in range(1, num):res = res + 1.0 / sqrt(i)return res
def main():start = time.time()res = func(30000000)print(f"res = {res}, use time {time.time() - start:.5}")if __name__ == '__main__':main()

Cython調用C/C++

既然C/C++比較高效, 我們能否直接用cython調用C/C++呢? 就是用C語言重寫一遍這個函數, 然后在cython里進行調用.

首先寫一段對應的C語言版本

usefunc.h

#pragma once
#include <math.h>
double c_func(int n)
{int i;double result = 0.0;for(i=1; i<n; i++)result = result + sqrt(i);return result;
}

然后, 我們在Cython中, 引入這個頭文件, 然后調用這個函數

cdef extern from "usecfunc.h":cdef double c_func(int n)
import timedef func(int n):return c_func(n)def main():start = time.time()res = func(30000000)print(f"res = {res}, use time {time.time() - start:.5}")

在Cython中使用numpy

Cython中, 我們可以調用numpy. 但是, 如果直接按照數組下標訪問, 我們還需要動態判斷numpy數據的類型, 這樣效率就比較低.

 import numpy as npcimport numpy as npfrom libc.math cimport sqrtimport timedef func(int n):cdef np.ndarray arr = np.empty(n, dtype=np.float64)cdef int i, num = n for i in range(1, num):arr[i] = 1.0 / sqrt(i)return arrdef main():start = time.time()res = func(30000000)print(f"len(res) = {len(res)}, use time {time.time() - start:.5}")

解釋:

 cimport numpy as np

這一句的意思是, 我們可以使用numpy的C/C++接口(指定數據類型, 數組維度等).

這一句的意思是, 我們也可以使用numpy的Python接口(np.array, np.linspace等). Cython在內部處理這種模糊性,這樣用戶就不需要使用不同的名稱.

在編譯的時候, 我們還需要修改setup.py, 引入numpy的頭文件.

from distutils.core import setup, Extension
from Cython.Build import cythonize
import numpy as npsetup(ext_modules = cythonize(Extension("RawPython4", ["RawPython4.pyx"],include_dirs=[np.get_include()],), language_level=3)
)

加速!加速!

上面的代碼, 還是能夠進一步加速的

  1. 可以指定numpy數組的數據類型和維度, 這樣就不用動態判斷數據類型了. 實際生成的代碼, 就是按C語言里按照數組下標來訪問.
  2. 在使用numpy數組時, 還要同時做數組越界檢查. 如果我們確定自己的程序不會越界, 可以關閉數組越界檢測.
  3. Python還支持負數下標訪問, 也就是從后往前的第i個. 為了做負數下標訪問, 也需要一個額外的if…else…來判斷. 如果我們用不到這個功能, 也可以關掉.
  4. Python還會做除以0的檢查, 我們并不會做除以0的事情, 關掉.
  5. 相關的檢查也關掉.

最終加速的程序如下:

import numpy as np
cimport numpy as np
from libc.math cimport sqrt
import time
cimport cython@cython.boundscheck(False)         # 關閉數組下標越界
@cython.wraparound(False)          # 關閉負索引
@cython.cdivision(True)            # 關閉除0檢查
@cython.initializedcheck(False)    # 關閉檢查內存視圖是否初始化
def func(int n):cdef np.ndarray[np.float64_t, ndim=1] arr = np.empty(n, dtype=np.float64)cdef int i, num = n for i in range(1, num):arr[i] = 1.0 / sqrt(i)return arrdef main():start = time.time()res = func(30000000)print(f"len(res) = {len(res)}, use time {time.time() - start:.5}")
cdef np.ndarray[np.float64_t, ndim=1] arr = np.empty(n, dtype=np.float64)

這一句的意思是, 我們創建numpy數組時, 手動指定變量類型和數組維度.

上面是對這一個函數關閉數組下標越界, 負索引, 除0檢查, 內存視圖是否初始化等. 我們也可以在全局范圍內設置, 即在.pyx文件的頭部, 加上注釋

# cython: boundscheck=False
# cython: wraparound=False
# cython: cdivision=True
# cython: initializedcheck=False

也可以用這種寫法:

with cython.cdivision(True):# do something here

其他

cython吸收了很多C/C++的語法, 也包括指針和引用. 也可以把一個struct/class從C++傳給Cython.

Cython總結

Cython的語法與Python類似, 同時引入了一些C/C++的特性, 比如指定變量類型等. 同時, Cython還可以調用C/C++的函數.

Cython的特點在于, 如果沒有指定變量類型, 執行效率跟Python差不多. 指定好類型后, 執行效率才會比較高.

更多文檔可以參考Cython官方文檔

Welcome to Cython’s Documentationdocs.cython.org/en/latest/index.html

pybind11

Cython是一種類Python的語言, 但是pybind11是基于C++的. 我們在.cpp文件中引入pybind11, 定義python程序入口, 然后編譯執行就好了.

從官網的說明中看到pybind11的幾個特點

  • 輕量級頭文件庫
  • 目標和語法類似于優秀的Boost.python庫
  • 用于為python綁定c++代碼

安裝

可以執行pip install pybind11安裝 pybind11 (萬能的pip)

也可以用Visual Studio + vcpkg+CMake來安裝.

簡單的例子

#include <pybind11/pybind11.h>namespace py = pybind11;
int add_func(int i, int j) {return i + j;
}PYBIND11_MODULE(example, m) {m.doc() = "pybind11 example plugin";  //可選,說明這個模塊是做什么的m.def("add_func", &add_func, "A function which adds two numbers");
}

首先引入pybind11的頭文件, 然后用PYBIND11_MODULE聲明.

  • example:模型名,切記不需要引號. 之后可以在python中執行import example
  • m:可以理解成模塊對象, 用于給Python提供接口
  • m.doc():help說明
  • m.def:用來注冊函數和Python打通界限
m.def( "給python調用方法名"&實際操作的函數, "函數功能說明" ). //其中函數功能說明為可選

編譯&運行

pybind11只有頭文件,所以只要在代碼中增加相應的頭文件, 就可以使用pybind11了.

#include <pybind11/pybind11.h>

在Linux下, 可以執行這樣的命令來編譯:

 c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

我們也可以用setup.py來編譯(在Windows下, 需要Visual Studio或mingw等編譯工具; 在Linux或是Mac下, 需要gcc或clang等編譯工具)

from setuptools import setup, Extension
import pybind11functions_module = Extension(name='example',sources=['example.cpp'],include_dirs=[pybind11.get_include()],
)setup(ext_modules=[functions_module])

然后運行下面的命令, 就可以編譯了

python setup.py build_ext --inplace

在python中進行調用

python -c "import example; print(example.add_func(200, 33))"

在pybind11中指定函數參數

通過簡單的代碼修改,就可以通知Python參數名稱

m.def("add", &add, "A function which adds two numbers", py::arg("i"), py::arg("j"));

也可以指定默認參數

int add(int i = 1, int j = 2) {return i + j;
}

PYBIND11_MODULE中指定默認參數

m.def("add", &add, "A function which adds two numbers",py::arg("i") = 1, py::arg("j") = 2);

為Python方法添加變量

PYBIND11_MODULE(example, m) {m.attr("the_answer") = 23333;py::object world = py::cast("World");m.attr("what") = world;
}

對于字符串, 需要用py::cast將其轉化為Python對象.

然后在Python中, 可以訪問the_answerwhat對象

import example
>>>example.the_answer
42
>>>example.what
'World'

在cpp文件中調用python方法

因為python萬物皆為對象, 因此我們可以用py::object 來保存Python中的變量/方法/模塊等.

 py::object os = py::module_::import("os");py::object makedirs = os.attr("makedirs");makedirs("/tmp/path/to/somewhere");

這就相當于在Python里執行了

 import osmakedirs = os.makedirsmakedirs("/tmp/path/to/somewhere")

用pybind11使用python list

我們可以直接傳入python的list

 void print_list(py::list my_list) {for (auto item : my_list)py::print(item);}PYBIND11_MODULE(example, m) {m.def("print_list", &print_list, "function to print list", py::arg("my_list"));}

在Python里跑一下這個程序,

 >>>import example>>>result = example.print_list([2, 23, 233])2 23 233>>>print(result)

這個函數也可以用std::vector<int>作為參數. 為什么可以這樣做呢? pybind11可以自動將python list對象, 復制構造為std::vector<int>. 在返回的時候, 又自動地把std::vector轉化為Python中的list. 代碼如下:

 #include <pybind11/pybind11.h>#include <pybind11/stl.h>std::vector<int> print_list2(std::vector<int> & my_list) {auto x = std::vector<int>();for (auto item : my_list){x.push_back(item + 233);}return x;}PYBIND11_MODULE(example, m) {m.def("print_list2", &print_list2, "help message", py::arg("my_list"));}

用pybind11使用numpy

因為numpy比較好用, 所以如果能夠把numpy數組作為參數傳給pybind11, 那就非常香了. 代碼如下(一大段)

 #include <pybind11/pybind11.h>#include <pybind11/numpy.h>py::array_t<double> add_arrays(py::array_t<double> input1, py::array_t<double> input2) {py::buffer_info buf1 = input1.request(), buf2 = input2.request();if (buf1.ndim != 1 || buf2.ndim != 1)throw std::runtime_error("Number of dimensions must be one");if (buf1.size != buf2.size)throw std::runtime_error("Input shapes must match");/* No pointer is passed, so NumPy will allocate the buffer */auto result = py::array_t<double>(buf1.size);py::buffer_info buf3 = result.request();double *ptr1 = (double *) buf1.ptr,*ptr2 = (double *) buf2.ptr,*ptr3 = (double *) buf3.ptr;for (size_t idx = 0; idx < buf1.shape[0]; idx++)ptr3[idx] = ptr1[idx] + ptr2[idx];return result;}m.def("add_arrays", &add_arrays, "Add two NumPy arrays");

先把numpy的指針拿出來, 然后在指針上進行操作.

我們在Python里測試如下:

 >>>import example>>>import numpy as np>>>x = np.ones(3)>>>y = np.ones(3)>>>z = example.add_arrays(x, y)>>>print(type(z))<class 'numpy.ndarray'>>>>print(z)array([2., 2., 2.])

來一段完整的代碼

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>namespace py = pybind11;
int add_func(int i, int j) {return i + j;
}void print_list(py::list my_list) {for (auto item : my_list)py::print(item);
}std::vector<int> print_list2(std::vector<int> & my_list) {auto x = std::vector<int>();for (auto item : my_list){x.push_back(item + 233);}return x;
}py::array_t<double> add_arrays(py::array_t<double> input1, py::array_t<double> input2) {py::buffer_info buf1 = input1.request(), buf2 = input2.request();if (buf1.ndim != 1 || buf2.ndim != 1)throw std::runtime_error("Number of dimensions must be one");if (buf1.size != buf2.size)throw std::runtime_error("Input shapes must match");/* No pointer is passed, so NumPy will allocate the buffer */auto result = py::array_t<double>(buf1.size);py::buffer_info buf3 = result.request();double *ptr1 = (double *) buf1.ptr,*ptr2 = (double *) buf2.ptr,*ptr3 = (double *) buf3.ptr;for (size_t idx = 0; idx < buf1.shape[0]; idx++)ptr3[idx] = ptr1[idx] + ptr2[idx];return result;
}PYBIND11_MODULE(example, m) {m.doc() = "pybind11 example plugin";  //可選,說明這個模塊是做什么的m.def("add_func", &add_func, "A function which adds two numbers");m.attr("the_answer") = 23333;py::object world = py::cast("World");m.attr("what") = world;m.def("print_list", &print_list, "function to print list", py::arg("my_list"));m.def("print_list2", &print_list2, "help message", py::arg("my_list2"));m.def("add_arrays", &add_arrays, "Add two NumPy arrays");
}

pybind11總結

pybind11在C++下使用, 可以為Python程序提供C++接口. 同時, pybind11也支持傳入python list, numpy等對象.

更多文檔可以參考pybind11官方文檔

https://pybind11.readthedocs.io/en/stable/pybind11.readthedocs.io/en/stable/

其他使用python調用C++的方式

  1. CPython會自帶一個Python.h, 我們可以在C/C++中引入這個頭文件, 然后編譯生成動態鏈接庫. 但是, 直接調用Python.h寫起來有一點點麻煩.
  2. boost是一個C++庫, 對Python.h做了封裝, 但整個boost庫比較龐大, 而且相關的文檔不太友好.
  3. swig(Simplified Wrapper and Interface Generator), 用特定的語法聲明C/C++函數/變量. (之前tensorlfow用的就是這個, 但現在改成pybind11了)

總結: 什么時候應該加速呢

用Python開發比較簡潔, 用C++開發寫起來有些麻煩.

在寫python時, 我們可以通過Profile等耗時分析工具, 找出比較用時的代碼塊, 對這一塊用C++進行優化. 沒必要優化所有的部分.

Cython或是pybind11只做三件事: 加速, 加速, 還是加速. 在需要大量計算, 比較耗時的地方, 我們可以用C/C++來實現, 這樣有助于提升整個Python程序的執行速度.

加速python還有一些其他的方法, 比如用numpy的向量化操作代替for循環, 使用jit即時編譯等.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/532460.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/532460.shtml
英文地址,請注明出處:http://en.pswp.cn/news/532460.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Pytorch導出onnx模型,C++轉化為TensorRT并實現推理過程

Pytorch導出onnx模型&#xff0c;C轉化為TensorRT并實現推理過程 前言 本文為旨在實現整個Python導出PyTorch模型&#xff0c;C轉化為TensorRT并實現推理過程過程&#xff0c;只與模型推理&#xff0c;模型部署相關&#xff0c;不涉及模型訓練。為突出整個部署過程而非具體模…

從零Makefile落地算法大項目,完整案例教程

從零Makefile落地算法大項目&#xff0c;完整案例教程 轉自&#xff1a;從零Makefile落地算法大項目&#xff0c;完整案例教程 作者&#xff1a;手寫AI 前言 在這里&#xff0c;你能學到基于Makefile的正式大項目的使用方式和考慮&#xff0c;相信我&#xff0c;其實可以很簡單…

PyTorch擴展自定義PyThonC++(CUDA)算子的若干方法總結

PyTorch擴展自定義PyThon/C(CUDA)算子的若干方法總結 轉自&#xff1a;https://zhuanlan.zhihu.com/p/158643792 作者&#xff1a;奔騰的黑貓 在做畢設的時候需要實現一個PyTorch原生代碼中沒有的并行算子&#xff0c;所以用到了這部分的知識&#xff0c;再不總結就要忘光了 &a…

給 Python 算法插上性能的翅膀——pybind11 落地實踐

給 Python 算法插上性能的翅膀——pybind11 落地實踐 轉自&#xff1a;https://zhuanlan.zhihu.com/p/444805518 作者&#xff1a;jesonxiang&#xff08;向乾彪&#xff09;&#xff0c;騰訊 TEG 后臺開發工程師 1. 背景 目前 AI 算法開發特別是訓練基本都以 Python 為主&…

chrome自動提交文件_收集文檔及提交名單統計

知乎文章若有排版問題請見諒&#xff0c;原文放在個人博客中【歡迎互踩&#xff01;】文叔叔文檔收集使用動機在我們的學習工作中&#xff0c;少不了要讓大家集體提交文件的情況&#xff0c;舉個最簡單的例子&#xff1a;收作業。 傳統的文件收集流程大致是&#xff1a;群內發出…

Pytorch自定義C++/CUDA擴展

Pytorch自定義C/CUDA擴展 翻譯自&#xff1a;官方文檔 PyTorch 提供了大量與神經網絡、張量代數、數據整理和其他操作。但是&#xff0c;我們有時會需要更加定制化的操作。例如&#xff0c;想要使用論文中找到的一種新型的激活函數&#xff0c;或者實現自己設計的算子。 在 Py…

惠普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…

h5頁面禁止復制_H5移動端頁面禁止復制技巧

前言&#xff1a;業務需要&#xff0c;需要對整個頁面禁止彈出復制菜單。在禁止的頁面中加入以下css樣式定義* {-webkit-touch-callout:none;/*系統默認菜單被禁用*/-webkit-user-select:none;/*webkit瀏覽器*/-khtml-user-select:none;/*早起瀏覽器*/-moz-user-select:none;/*…

梯度下降法和牛頓法計算開根號

梯度下降法和牛頓法計算開根號 本文將介紹如何不調包&#xff0c;只能使用加減乘除法實現對根號x的求解。主要介紹梯度下降和牛頓法者兩種方法&#xff0c;并給出 C 實現。 梯度下降法 思路/步驟 轉化問題&#xff0c;將 x\sqrt{x}x? 的求解轉化為最小化目標函數&#xff…